SS241102C. 墙(wall)

SS241102C. 墙(wall)

原题:P10764 [BalticOI 2024] Wall

题意

给你一面墙,第 \(i\) 列的高度是 \(a_i\) 或者 \(b_i\)。你在墙上装水。若第 \(i\) 列的墙的高度是 \(h_i\),位置 \(i\) 的积水高度是 \(H_i\) 当且仅当存在 \(h_{j<i}\ge H_i\)\(h_{j>i} \ge H_i\)

求所有情况的 \(\sum H_i-h_i\)

\(10^9 + 7\) 取模。\(n\le 3\times 10^5\)

solution

拜谢 cjh。

暴力是枚举所有情况,然后直接枚举 \(i\) 来求。

我们把枚举情况写进求和符号内,变成求合法方案数。

\[\sum_{i=1}^n \sum_{j=1}^V H_i \times (H_i=j-h_i 的方案数) \]

其中 \(V\) 是值域最大值。

拆开贡献:

\[\sum_{i=1}^n \sum_{j=1}^V (j 的高度处有水(h_i 处算没水)的方案数) \]

这样一个 \(H_i\) 就会在 \(j=h_i+1\sim h_i+H_i\) 的时候计贡献了。

如何求 \((j 的高度处有水(h_i 处算没水)的方案数)\)

就是求左边最高的墙高 \(\ge j\) 且右边最高的墙高 \(\ge j\) 的方案数。

同时需要乘上一个第 \(i\) 个墙选择 \(a_i/b_i\) 的系数(\(h_i\) 必须 \(<\) j)。

即:

\[\sum_{i=1}^{n} \sum_{j=1}^V L_{i,j} R_{i,j} ([a_i< j]+[b_i< j]) \]

其中 \(L_{i,j}\) 表示 \(i\) 左边的最高的墙 \(\ge j\) 的方案数,\(R\) 类似。

然而最高 \(\ge j\) 不好算,但是 \(< j\) 是好算的。

因此设 \(L_{i,j}\) 表示 \(i\) 左边的最高的墙 \(< j\) 的方案数,\(R\) 类似。

有:

\[L_{i,j}=\prod_{k=1}^{i-1} [a_k<j]+[b_k<j]\\ R_{i,j}=\prod_{k=i+1}^{n} [a_k<j]+[b_k<j]\\ ans=\sum_{i=1}^n \sum_{j=1}^V (2^{i-1} - L_{i,j})(2^{n-i} - R_{i,j}) ([a_i< j]+[b_i< j]) \]

\(c_i=([a_i< j]+[b_i< j])\),有:

\[\begin{aligned} ans & =\sum_{i=1}^n \sum_{j=1}^V (2^{i-1} - L_{i,j})(2^{n-i} - R_{i,j}) c_i\\ & = \sum_{i=1}^n \sum_{j=1}^V 2^{n-1}c_i + L_{i,j}R_{i,j}c_i - 2^{n-i} L_{i,j}c_i - 2^{i-1}R_{i,j}c_i \end{aligned} \]

于是我们要选择枚举 \(i\) 或者 \(j\),然后维护另一维。

我们选择倒序枚举 \(j\)

事实上其他枚举方式也是可以做的,但是比较麻烦,比如枚举 \(i\) 会出现除以 \(0\) 的操作。其他枚举方式都是不方便做的(maybe 需要维护的信息比较复杂?),至少倒序枚举 \(j\) 一定方便做。

\[\sum_{1}^{j=V} \sum_{i=1}^n 2^{n-1}c_i + (L_{i,j}R_{i,j}c_i) - (2^{n-i} L_{i,j}c_i) - (2^{i-1}R_{i,j}c_i) \]

我们要分别维护括号的三个东西。

使用线段树维护。

实际上想到使用线段树,应该是因为之前使用 \(L_{i,j}\) 的时候就发现很适合使用线段树。

\[L_{i,j}=\prod_{k=1}^{i-1} [a_k<j]+[b_k<j]\\ R_{i,j}=\prod_{k=i+1}^{n} [a_k<j]+[b_k<j] \]

抄过来看看。

\(j\) 变小的时候,相当于对 \(L_i\) 执行后缀 \(\div 2\) 或者 \(\times 0\) 的操作,以及对 \(R_i\) 的某些前缀执行这样的操作。

以及 \(c_i\) 也会变化,单点修改就行。

这样就可以维护了。

结论:倒序枚举值域,线段树下标维护数组下标。枚举值域,对影响到的下标,对线段树执行前缀或者后缀操作。然后求线段树全局和。线段树维护三个信息。

时间复杂度 \(O(n\log n)\)

写起来还是……相对……也许……可能……实际上应该是挺好写的,但是感觉推式子细节比较多?应该是我太菜了的缘故。

code

我用了这么多线段树,没有任何优化(为了保证代码可读性,保证我这种蒟蒻能读懂自己的代码……),但是为什么跑到目前最优解?(大雾)

#include<bits/stdc++.h>
// #define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
constexpr int N=5e5+7,mod=1e9+7,inv=500000004;
int add(int a,int b) { return a+b>=mod ? a+b-mod : a+b; }
void _add(int &a,int b) { a=add(a,b); }
ll ksm(ll a,ll b=mod-2) {
	ll s=1;
	while(b) {
		if(b&1) s=s*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return s;
}
int n,m;
struct pii {
	int a,b;
}x[N];
struct pib {
	int id;
	bool ab;
};
vector<pib> vec[N<<1];
int c[N<<1];
int val;
struct tree {
	int tr[N<<2];
	int div[N<<2];
	bool del[N<<2];
	void maketag(int u,int t1,bool t2) {
		if(t2) tr[u]=0,del[u]=1;
		else tr[u]=1ll*tr[u]*t1%mod,div[u]=1ll*div[u]*t1%mod;
	}
	void pushup(int u) {
		tr[u]=add(tr[u<<1],tr[u<<1|1]);
	}
	void pushdown(int u) {
		maketag(u<<1,div[u],del[u]), maketag(u<<1|1,div[u],del[u]);
		div[u]=1;
	}
	void build(int u,int l,int r) {
		div[u]=1;
		if(l==r) { tr[u]=val; return; }
		int mid=(l+r)>>1;
		build(u<<1,l,mid),build(u<<1|1,mid+1,r);
		pushup(u);
	}
	void change (int u,int l,int r,int L,int R,int t1,bool t2) {
		if(del[u]) return;
		if(l>=L&&r<=R) { maketag(u,t1,t2); return; }
		int mid=(l+r)>>1;
		pushdown(u);
		if(L<=mid) change(u<<1,l,mid,L,R,t1,t2);
		if(mid+1<=R) change(u<<1|1,mid+1,r,L,R,t1,t2);
		pushup(u);
	}
	void change (int u,int l,int r,int x,int t1,bool t2) {
		if(del[u]) return;
		if(l==r) { maketag(u,t1,t2); return; }
		int mid=(l+r)>>1;
		pushdown(u);
		if(x<=mid) change(u<<1,l,mid,x,t1,t2);
		else change(u<<1|1,mid+1,r,x,t1,t2);
		pushup(u);
	}
}T1,T2,T3;
int cnt,ans;
int main() {
	#ifdef LOCAL
	freopen("my.out","w",stdout);
	#else 
	freopen("wall.in","r",stdin);
	freopen("wall.out","w",stdout);
	#endif
	sf("%d",&n);
	rep(i,1,n) sf("%d",&x[i].a);
	rep(i,1,n) {
		sf("%d",&x[i].b);
		if(x[i].a > x[i].b) swap(x[i].a,x[i].b);
		c[i*2-1]=x[i].a, c[i*2]=x[i].b;
	}
	sort(c+1,c+(n<<1)+1);
	m=unique(c+1,c+(n<<1)+1)-c-1;
	rep(i,1,n) {
		x[i].a=lower_bound(c+1,c+m+1,x[i].a)-c;
		x[i].b=lower_bound(c+1,c+m+1,x[i].b)-c;
		vec[x[i].a].push_back({i,0}), vec[x[i].b].push_back({i,1});
	}
	val=ksm(2,n);
	T1.build(1,1,n),T2.build(1,1,n),T3.build(1,1,n);
	cnt=n<<1;
	val=1ll*val*inv%mod;
	per(i,m,1) {
		for(auto u : vec[i]) {
			int id=u.id;
			bool op=u.ab;
			--cnt;
			if(op) {
				T1.change(1,1,n,id,inv,0),
				T2.change(1,1,n,id,inv,0),
				T3.change(1,1,n,id,inv,0);
				if(id<n) T1.change(1,1,n,id+1,n,inv,0), T2.change(1,1,n,id+1,n,inv,0);
				if(id>1) T1.change(1,1,n,1,id-1,inv,0), T3.change(1,1,n,1,id-1,inv,0);
			}else{
				T1.change(1,1,n,id,0,1), 
				T2.change(1,1,n,id,0,1),
				T3.change(1,1,n,id,0,1);
				if(id<n) T1.change(1,1,n,id+1,n,0,1), T2.change(1,1,n,id+1,n,0,1);
				if(id>1) T1.change(1,1,n,1,id-1,0,1), T3.change(1,1,n,1,id-1,0,1);
			}
		}
		int s=0;
		_add(s,1ll*val*cnt%mod);
		_add(s,T1.tr[1]);
		_add(s,mod-add(T2.tr[1],T3.tr[1]));
		_add(ans,1ll*s*(c[i]-c[i-1])%mod);
	}
	pf("%d\n",ans);
}
posted @ 2024-11-02 16:44  liyixin  阅读(19)  评论(0编辑  收藏  举报