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;
}