題解:P10764 [BalticOI 2024] Wall

ffffyc發表於2024-07-24

\(\text{Link}\)

題意

給你兩個長為 \(n\) 的序列 \(a_{i,0/1}\),有 \(n\) 個磚塊牆,其中牆 \(i\) 的高度可能為 \(a_i\) 或者 \(b_i\),請求出每種情況下這些牆積水深度的總和。

形式化地,設牆 \(i\) 的高度為 \(h_i\),則最終牆 \(i\)水面高度\(\displaystyle\min\left(\max_{j=1}^{i}h_j,\max_{j=i}^nh_j\right)\)

\(n\le 5\times 10^5\)

題解

觀察一下題面給的圖並手玩一會可以發現字首最大值和字尾最大值是重要的。更具體地,設某種情況下牆 \(i\) 的高度為 \(h_i\),設字首最大值的位置分別為 \(p_{1\sim k}\),則 \((p_i,p_{i+1})\) 內的牆的積水水面高度均為 \(p_i\)。字尾最大值是同理的。於是我們要設計某種與字首最大值有關的狀態,同時為了防止算重,對於兩個相同的高度,我們令後面的大於前面的。

\(f_{i,0/1}\) 表示 \(h_i=a_{i,0/1}\) 是一個字首最大值時字首 \(i\) 的所有情況的水面高度和,\(c_{i,0/1}\) 為此時的方案數。

我們考慮如何從 \(i\) 轉移到 \(j\),不妨以 \((i,p)\) 轉移到 \((j,q)\) 為例,其中 \(p,q=0/1\)

  • 必須滿足 \(a_{i,p}\le a_{j,q}\)\(\forall k\in(i,j),\min(a_{k,0},a_{k,1})<a_{i,p}\)
  • 考慮 \((i,j)\) 內的方案數,令 \(t=\sum_{k=i+1}^{j-1}[\max(a_{k,0},a_{k,1})<a_{i,p}]\),則 \((i,j)\) 內有 \(t\) 個位置可任意選擇,方案數為 \(2^t\)
  • 新增的水面高度為 \((j-i)a_{i,p}\)

經過一些嘗試,以下標順序轉移是困難的。考慮到我們轉移過程中要涉及權值比較且只能從小權值轉移到大權值,於是我們不妨考慮按值域從小到大轉移。轉移中與 \(i\) 相關的資訊較多,我們使用刷表法

我們把所有的 \(a_{i,0/1}\) 排序後從小到大掃,設現在掃到 \(a_{i,p}\),考慮向後更新。設 \(r_i\) 表示已經掃到了幾個 \(a_{i,p}\)\(t_i\) 表示 \([1,i]\) 有幾個已經被掃到了 \(2\) 次即 \(\sum_{j=1}^i[r_j=2]\)。用並查集找到 \(i\) 後第一個 \(r_j=0\)\(j\),則對於 \(k\in(i,j],q\in[0,1]\)\(a_{k,q}\) 還未被掃到,\(f_{k,q}\gets f_{k,q}+2^{t_k-t_i}[f_{i,p}+(k-i)a_{i,p}]\)\(c_{k,q}\gets c_{k,q}\cdot 2^{t_k-t_i}c_{i,p}\)。拆掉括號,分離 \(i,k\) 後可以使用線段樹維護。

實現中許多邊界情況不需要我們考慮,比如上述式子中 \(t_k\) 應為 \(t_{k-1}\),但 \(a_{k,q}\) 還未掃到即 \(r_k\ne 2,t_k=t_{k-1}\);又如我們不需要分別處理已被掃到和未被掃到、\(p,q=0/1\) 之類的問題,更具體可以參考程式碼。

時間複雜度 \(O(n\log n)\)

參考程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
	
}
#define pii pair<int,int>
#define mpr make_pair
#define fir first
#define sec second
const int N=5e5+10,mod=1e9+7,inv2=(mod+1)/2;
inline int add(int x,int y){ return x+y>=mod?x+y-mod:x+y; }
inline int dec(int x,int y){ return x>=y?x-y:x+mod-y; }
struct node{
	int v,p;
	inline friend bool operator<(const node &a,const node &b){
		return a.v==b.v?a.p<b.p:a.v<b.v;
	}
}a[N<<1];
struct TD{
	int a,b,c;
	TD(int A=0,int B=0,int C=0){ a=A,b=B,c=C; }
	inline friend TD operator +(const TD &a,const TD &b){
		return TD(add(a.a,b.a),add(a.b,b.b),add(a.c,b.c));
	}
	inline friend TD operator *(const TD &a,const int &b){
		return TD(1ll*a.a*b%mod,1ll*a.b*b%mod,1ll*a.c*b%mod);
	}
};
int n,pw[N],ipw[N],fa[N],c[N],f[N][2],p[N][2],g[N][2],q[N][2];
inline int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); }
struct BIT{
	#define lowbit(x) (x&-x)
	int t[N];
	inline void clear(){
		for(int i=1;i<=n;i++)
			t[i]=0;
	}
	inline void add(int x,int v){
		for(int i=x;i<=n;i+=lowbit(i))
			t[i]+=v;
	}
	inline int query(int x){
		int s=0;
		for(int i=x;i;i-=lowbit(i))
			s+=t[i];
		return s;
	}
}T0;
struct Segment_Tree{
	#define ls (rt<<1)
	#define rs (rt<<1|1)
	TD vs[N<<2];
	int tag[N<<2];
	inline void pushadd(int rt,TD v){
		vs[rt]=vs[rt]+v*tag[rt];
	}
	inline void pushtag(int rt,int v){
		tag[rt]=1ll*tag[rt]*v%mod;
	}
	inline void pushdown(int rt){
		if(vs[rt].a||vs[rt].b||vs[rt].c){
			pushadd(ls,vs[rt]);
			pushadd(rs,vs[rt]);
			vs[rt]=TD();
		}
		if(tag[rt]!=1){
			pushtag(ls,tag[rt]);
			pushtag(rs,tag[rt]);
			tag[rt]=1;
		}
	}
	inline void build(int rt,int l,int r){
		tag[rt]=1,vs[rt]=TD();
		if(l==r){ return ; }
		int mid=l+r>>1;
		build(ls,l,mid),build(rs,mid+1,r);
	}
	inline void add(int rt,int l,int r,int L,int R,TD v){
		if(L>R) return ;
		if(L<=l&&r<=R) return pushadd(rt,v);
		pushdown(rt);
		int mid=l+r>>1;
		if(L<=mid) add(ls,l,mid,L,R,v);
		if(R>mid) add(rs,mid+1,r,L,R,v);
	}
	inline void mul(int rt,int l,int r,int L,int R,int v){
		if(L>R) return ;
		if(L<=l&&r<=R) return pushtag(rt,v);
		pushdown(rt);
		int mid=l+r>>1;
		if(L<=mid) mul(ls,l,mid,L,R,v);
		if(R>mid) mul(rs,mid+1,r,L,R,v);
	}
	inline TD query(int rt,int l,int r,int p){
		if(l==r) return vs[rt];
		pushdown(rt);
		int mid=l+r>>1;
		if(p<=mid) return query(ls,l,mid,p);
		else return query(rs,mid+1,r,p);
	}
}T;
int main(){
	n=read();
	for(int i=1;i<=n;i++)
		a[i]={read(),i};
	for(int i=1;i<=n;i++)
		a[i+n]={read(),i};
	pw[0]=ipw[0]=1;
	for(int i=1;i<=n;i++)
		pw[i]=add(pw[i-1],pw[i-1]),
		ipw[i]=1ll*inv2*ipw[i-1]%mod;
	sort(a+1,a+n*2+1);
	for(int i=0;i<=n+1;i++)
		fa[i]=i;
	T.build(1,1,n);
	T.add(1,1,n,1,1,TD(0,0,1));
	for(int i=1;i<=n*2;i++){
		int v=a[i].v,t=a[i].p;
		TD res=T.query(1,1,n,t);
		f[t][c[t]]=(res.a+1ll*t*res.b)%mod;
		p[t][c[t]]=res.c;
		int ct=T0.query(t);
		int pt=find(t+1);
		if(pt==n+1) pt=n;
		T.add(1,1,n,t+1,pt,TD(dec(f[t][c[t]],1ll*t*v%mod*p[t][c[t]]%mod),1ll*v*p[t][c[t]]%mod,p[t][c[t]])*ipw[ct]);
		c[t]++;
		if(c[t]==1) fa[t]=t+1;
		if(c[t]==2) T0.add(t,1),T.mul(1,1,n,t,n,2);
	}
	for(int i=0;i<=n+1;i++)
		fa[i]=i,c[i]=0;
	T0.clear(),T.build(1,1,n);
	T.add(1,1,n,n,n,TD(0,0,1));
	int ans=0;
	for(int i=1;i<=n*2;i++){
		int v=a[i].v,t=a[i].p;
		TD res=T.query(1,1,n,t);
		g[t][c[t]]=(res.a+1ll*t*res.b)%mod;
		q[t][c[t]]=res.c;
		ans=(ans+1ll*v*p[t][c[t]]%mod*q[t][c[t]])%mod;
		int ct=T0.query(t);
		int pt=find(t-1);
		if(pt==0) pt=1;
		T.add(1,1,n,pt,t-1,TD(add(g[t][c[t]],1ll*t*v%mod*q[t][c[t]]%mod),mod-1ll*v*q[t][c[t]]%mod,q[t][c[t]])*pw[ct]);
		c[t]++;
		if(c[t]==1) fa[t]=t-1;
		if(c[t]==2) T0.add(t,1),T.mul(1,1,n,t,n,inv2);
	}
	for(int i=1;i<=n*2;i++)
		ans=dec(ans,1ll*pw[n-1]*a[i].v%mod);
	for(int i=1;i<=n;i++)
		ans=(ans+1ll*f[i][0]*q[i][0]+1ll*p[i][0]*g[i][0]+1ll*f[i][1]*q[i][1]+1ll*p[i][1]*g[i][1])%mod;
	write(ans);
	flush();
}

相關文章