P8421 解题报告
好题。
考虑离线:
- Kira:统计一个区间 \([l,r]\) 的所有子区间权值和可以转化为 \(f_{r,r}-f_{r,l-1}\),其中 \(f_{i,j}\) 表示 \(l\le i,r \le j\) 的区间 \([l,r]\) 的权值和。
然后考虑已知 \(f_r\) 快速计算 \(f_{r+1}\)。
对于 \(f_{r,i} \to f_{r+1,i}\),其增加了 \([i,r+1]\),\(\forall i\in[1,r+1]\) 的区间的权值。
按位与,按位或,\(\gcd\),很难不让人想到势能分析。
不难得出,\(f_{r,i} \to f_{r+1,i}\) 时,必定存在一个 \(p\in[1,r+1]\),使得:\(\forall i<p\),\([i,r+1]\) 的权值和 \([i,r]\) 相同,且 \(\forall i \in[p,r+1]\),\([i,r+1]\) 的权值与 \([i,r]\) 不同。
而且这个 \(p\) 和 \(r+1\) 的距离还很近,是 \(\mathcal{O(\log n)}\) 级别的。
这启发我们对于 \([1,p)\) 的新增权值用懒标记的思想维护,\([p,r+1]\) 的暴力修改。
具体地,我们先将询问离线按 \(r\) 升序排序,然后对于每个 \(r\),暴力地找到对应的 \(p\),对 \([1,r]\) 从 \(p\) 劈开分成两部分处理。我们记录懒标记的标记时间戳,\([1,p)\) 段不用管,\([p,r+1]\) 段暴力修改时直接更新懒标记和对应的标记时刻,查询时用当前时刻减去标记时刻即可得知懒标记的作用时长。
复杂度 \(\mathcal{O(n\log V + m\log m)}\)。
细节看代码。
const int N=1e6+3,M=5e6+3;
int n,m,a[N],b[N],c[N],t[N];
sd array<int,3>q[M];
ui s[N],ans[M],tg[N];
il ui _Q(int x,ui tt){return s[x]+tg[x]*(tt-t[x]);}
il void Solve()
{
rd(n),rd(m);
for(int i=1;i<=n;++i) rd(a[i]);
for(int i=1;i<=n;++i) rd(b[i]);
for(int i=1;i<=n;++i) rd(c[i]);
for(int i=1,l,r;i<=m;++i) rd(l),rd(r),q[i]={r,l,i};
sd stable_sort(q+1,q+m+1);
for(int r=1,i=0,pos;r<=n;++r)
{
for(pos=r;pos-1;--pos)
{
if((a[pos]&a[pos-1])==a[pos-1]&&(b[pos]|b[pos-1])==b[pos-1]&&sd __gcd(c[pos],c[pos-1])==c[pos-1]) break;
a[pos-1]&=a[pos],b[pos-1]|=b[pos],c[pos-1]=sd __gcd(c[pos],c[pos-1]);
}
s[r]=_Q(r-1,r);
for(int j=pos;j<=r;++j) s[j]=_Q(j,r),tg[j]=tg[j-1]+1ull*a[j]*b[j]*c[j],t[j]=r;
for(;i<m&&r==q[i+1][0];++i,ans[q[i][2]]=_Q(q[i][0],r+1)-_Q(q[i][1]-1,r+1));
}
for(int i=1;i<=m;++i) wrt(ans[i],'\n');
return;
}

浙公网安备 33010602011771号