CF1172F Nauuo and Bug 题解

首先将问题转化为执行 sum(A,l,r,p) 后减去了多少次 \(p\)

注意到若 \(x>y\),则 \(result\) 的初值为 \(x\) 时执行 sum(A,l,r,p) 中减去 \(p\) 的次数不少于 \(result\) 的初值为 \(y\) 时执行 sum(A,l,r,p) 中减去 \(p\) 的次数,故考虑建线段树并对区间 \([l,r]\) 维护 \(c_x\) 表示执行 sum(A,l,r,p) 后恰好减少了 \(x\)\(p\)\(result\) 的最小值。

考虑如何 pushup,一种想法是枚举左儿子减去了 \(x\)\(p\),右儿子减去了 \(y\)\(p\) 进行合并,即若 \(c_{lson,x+1}-1+sum_{lson}-xp\ge c_{rson,y}\),则 \(c_{k,x+y}\)\(\max(c_{lson,x},c_{rson,y}-sum_{lson}+xp)\)\(\min\)

考虑如何优化,注意到 \(c\) 是随下标增加而不降的,故 \(c_{lson,x+1}\ge c_{lson,x}\),又因为 \(c_{lson,x+1}\ge c_{rson,y}-sum_{lson}+xp\),所以 \(c_{lson,x+1}\ge\max(c_{lson,x},c_{rson,y}-sum_{lson}+xp)\),因而 \(\max(c_{lson,x+1},c_{rson,y-1}-sum_{lson}+(x+1)p)\ge\max(c_{lson,x},c_{rson,y}-sum_{lson}+xp)\)。因此对于每个 \(c_{k,x+y}\) 只需在 \(x\) 最小时更新即可。

我们下面证明上述不等式的左侧也是随下标增加而不降的,即证 \(c_{x+1}-c_{x}\ge p\)

\(b_i\) 表示 \(result\) 的初值为 \(c_x\),对于序列 \(a_{1,..,n}\) 执行 sum(a,1,n,p) 时,第 \(9\) 行的 for 执行完第 \(i\) 次后 result 的值。记 \(d_i\) 表示 \(result\) 的初值为 \(c_x+p-1\),对于序列 \(a_{1,..,n}\) 执行 sum(a,1,n,p) 时,第 \(9\) 行的 for 执行完第 \(i\) 次后 result 的值。原命题等价于证明 \(b\) 中减去 \(p\) 的次数等于 \(c\) 中减去 \(p\) 的次数。

称一个位置是好的,当且仅当 \(result\) 在该位置上减去了 \(p\),可以发现 \(b\) 中至少有一个好的位置的值为 \(0\),否则将 \(c_x\)\(1\) 后减去 \(p\) 的次数仍然是 \(x\) 次,与 \(c_x\) 的定义不符。

找到第一个位置使得该位置在 \(b\) 中不是好的且在 \(d\) 中是好的,在此之前 \(d\) 中数都比 \(b\) 中对应位置的数大 \(p-1\),接下来的一段 \(d\) 中数都比 \(b\) 中对应位置的数小 \(1\),于是可以找到该位置后的第一个位置使得该位置在 \(b\) 中是好的且在 \(d\) 中不是好的,接下来的一段 \(d\) 中数又都比 \(b\) 中对应位置的数大 \(p-1\)……

于是我们发现,\(d\) 中的数要么比 \(b\) 中对应位置的数大 \(p-1\),要么小 \(1\)。更进一步,设最后一个好的 \(0\) 的位置是 \(t\),则 \(d_{t-1}=b_{t-1}+p-1\)\(d_{t-1}=b_{t-1}-1\),由于最后一个好的 \(0\)\(b\) 中是由 \(p\)\(p\) 得来的,故 \(b_{t-1}+a_t=p\),故 \(d_{t-1}+a_t=p-1\)\(d_{t-1}+a_t=2p-1\),而这两种情况都能推出 \(d_t=p-1\),也就是说在 \(t\) 及之前 \(b\)\(d\) 中的数减去 \(p\) 的次数是一样的。

\(c_x\)\(1\) 后类似之前可推出新的由 \(result\) 组成的序列 \(b'\) 满足 \(\forall 1\le i\le n\),有 \(b'_i=b_i-1\)\(b'_i=b_i+p-1\), 由 \(b_{t-1}+a_t=p\) 可得 \(b'_{t-1}+a_t=p-1\)\(b'_{t-1}+a_t=2p-1\),而这两种情况也都能推出 \(b'_t=p-1\)

\(c_x\) 的定义知 \(b'\) 中的数减去 \(p\) 的次数比 \(d\) 中的数减去 \(p\) 的次数少 \(1\),而我们又知道 \(b'\) 中的数在 \(t\) 及之前减去 \(p\) 的次数比 \(d\) 中的数在 \(t\) 及之前减去 \(p\) 的次数少 \(1\),故 \(b'\)\(b\)\(t\) 之后减去 \(p\) 的次数相等。又由于 \(b'\)\(d\)\(t\) 之后减去 \(p\) 的次数也相等,故 \(b\)\(d\)\(t\) 之后减去 \(p\) 的次数也相等。

因此,\(b\)\(d\) 中的数减去 \(p\) 的次数是一样的,原命题得证。

由上述结论可知 pushup 更新 \(c\) 可用双指针将复杂度降低至 \(O(r-l+1)\),之后查询在线段树上的 \(\log n\) 个节点上二分出减去 \(p\) 的次数,即可以 \(O(\log^2n)\) 的时间复杂度回答单词询问。故总时间复杂度 \(O(n\log n+m\log^2n)\)

Code

#include<bits/stdc++.h>
#define LL long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
int n,m;
LL p;
int a[1000002];
LL pre[1000002];
inline int lson(int x)
{
	return (x<<1);
}
inline int rson(int x)
{
	return ((x<<1)|1);
}
struct SegTree
{
	vector<LL> c[4000002];
	inline void build(int k,int l,int r)
	{
		if(l==r)return (void)(c[k].push_back(-inf),c[k].push_back(p-a[l]));
		int mid=((l+r)>>1),ls=lson(k),rs=rson(k);
		build(ls,l,mid),build(rs,mid+1,r);
		for(int i=0;i+1<c[ls].size()+c[rs].size();++i)c[k].push_back(i? inf:-inf);
		for(int i=0,j=0;i<c[ls].size();--j,++i)for(;j<c[rs].size() && (i+1==c[ls].size() || c[ls][i+1]-1+pre[mid]-pre[l-1]-p*i>=c[rs][j]);++j)c[k][i+j]=min(c[k][i+j],max(c[ls][i],c[rs][j]-pre[mid]+pre[l-1]+p*i));
	}
	inline int query(int k,int l,int r,int l1,int r1,LL d)
	{
		if(l>=l1 && r<=r1)
		{
			int L=0,R=c[k].size()-1,Mid;
			for(;L<=R;)
			{
				Mid=((L+R)>>1);
				if(c[k][Mid]<=d)L=Mid+1;
				else R=Mid-1;
			}
			return R;
		}
		int mid=((l+r)>>1),ls=lson(k),rs=rson(k),res;
		if(r1<=mid)return query(ls,l,mid,l1,r1,d);
		if(l1>mid)return query(rs,mid+1,r,l1,r1,d);
		return res=query(ls,l,mid,l1,mid,d),res+query(rs,mid+1,r,mid+1,r1,d+pre[mid]-pre[l1-1]-p*res);
	}
}S;
int main()
{
	scanf("%d%d%lld",&n,&m,&p);
	for(int i=1;i<=n;++i)scanf("%d",&a[i]),pre[i]=pre[i-1]+a[i];
	S.build(1,1,n);
	for(int x,y;m--;)scanf("%d%d",&x,&y),printf("%lld\n",pre[y]-pre[x-1]-p*S.query(1,1,n,x,y,0));
	return 0;
}
posted @ 2022-07-13 22:27  18Michael  阅读(81)  评论(0编辑  收藏  举报