P7722 [Ynoi2007] tmpq 题解
题目描述
给定长为 \(n\) 的数组 \(a,b,c\) ,接下来 \(m\) 次操作:
1 x y:将 \(a_x\) 改为 \(y\) 。2 x:求 \(\sum\limits_{1\le i\lt j\lt k\le x}[b_{a_i}=a_j=c_{a_k}]\) 。
数据范围
- \(1\le n\le 2\cdot 10^5,1\le m\le 5\cdot 10^4,1\le a_i,b_i,c_i,x,y\le n\) 。
时间限制 \(\texttt{4s}\) ,空间限制 \(\texttt{64MB}\) 。
分析
问题等价于每次对 \(a,b,c\) 单点修改,查询 \(\sum\limits_{1\le i\lt j\lt k\le x}[b_i=a_j=c_k]\) 。
离线所有操作,记录每个数在 \(a,b,c\) 中不同出现位置个数,并对此进行根号分治。
对于出现次数 \(\le B\) 的所有数 \(x\) ,可以用 \(\mathcal O(cnt_x)\) 的代价计算这个数的贡献。
具体的, \(f_{i,0/1/2}\) 表示考虑前 \(i\) 个位置,当前阶段为 \(b/a/c\) 的方案数,最后把 \(f_{i,2}\) 加入全局贡献即可。
修改时先撤销原本的贡献,重新跑 \(\texttt{dp}\) 然后加入新的贡献,要求支持 \(\mathcal O(n+mB)\) 次单点修改和 \(\mathcal O(m)\) 次查询前缀和,可以用分块平衡。
对于出现次数 \(\ge B\) 的所有数 \(x\) ,这样的数仅有 \(\frac{n+3m}B\) 个。
类比动态 \(\texttt{dp}\) ,用矩阵刻画转移,要求支持 \(\mathcal O(m)\) 次单点修改矩阵和 \(\mathcal O(m\frac{n+m}B)\) 次查询前缀积,也可以用分块平衡。
我们无法开下 \(\mathcal O(m\frac{n+m}B)\) 的空间,因此不能同时维护所有数的矩阵。
但是注意到可以离线,枚举每个 \(cnt_x\gt B\) 的数然后跑所有询问,可以做到空间线性。
时间复杂度 \(\mathcal O(mB+m\frac{n+m}B)=\mathcal O(m\sqrt{n+m})\) 。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,B=600;
int m,n;
int a[maxn],b[maxn],c[maxn],_a[maxn],_b[maxn],_c[maxn];
int bel[maxn],st[maxn],ed[maxn];
ll res[maxn];
vector<int> pos[maxn];
struct oper
{
int op,x,y;
}q[maxn];
inline int read()
{
int q=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) q=10*q+ch-'0',ch=getchar();
return q;
}
namespace block1
{
ll f[B+5][2],cur[maxn],sum[maxn];
inline void add(int x,ll v)
{
cur[x]+=v,sum[bel[x]]+=v;
}
inline ll query(int x)
{
ll res=0;
for(int i=1;i<bel[x];i++) res+=sum[i];
for(int i=st[bel[x]];i<=x;i++) res+=cur[i];
return res;
}
inline void upd(int x,int op)
{
if(pos[x].size()>B) return ;
for(int i=1;i<pos[x].size();i++)
{
f[i][0]=f[i-1][0]+(b[pos[x][i]]==x);
f[i][1]=f[i-1][1]+(a[pos[x][i]]==x?f[i-1][0]:0);
if(c[pos[x][i]]==x) block1::add(pos[x][i],op*f[i-1][1]);
}
}
}
namespace block2
{
int u;
struct mat
{
ll a,b,c,d,e,f;
}cur[maxn],sum[maxn];
inline mat operator*(mat x,mat y)
{
return {y.a+x.a,y.b+x.a*y.d+x.b,y.c+x.a*y.e+x.b*y.f+x.c,y.d+x.d,y.e+x.d*y.f+x.e,y.f+x.f};
}
inline mat get(int x)
{
return {b[x]==u,0,0,a[x]==u,0,c[x]==u};
}
inline void upd(int x)
{
for(int i=x;i<=ed[bel[x]];i++) cur[i]=i!=st[bel[x]]?cur[i-1]*get(i):get(i);
for(int i=bel[x];i<=bel[n];i++) sum[i]=sum[i-1]*cur[ed[i]];
}
inline ll query(int x)
{
return (sum[bel[x]-1]*cur[x]).c;
}
inline void solve(int _u)
{
if(pos[u=_u].size()<=B) return ;
for(int i=1;i<=n;i++) a[i]=_a[i],b[i]=_b[a[i]],c[i]=_c[a[i]];
for(int i=1;i<=n;i++) cur[i]=i!=st[bel[i]]?cur[i-1]*get(i):get(i);
for(int i=1;i<=bel[n];i++) sum[i]=sum[i-1]*cur[ed[i]];
for(int i=1;i<=m;i++)
{
int x=q[i].x,y=q[i].y;
if(q[i].op==1)
{
int flg=a[x]==u||b[x]==u||c[x]==u||y==u||_b[y]==u||_c[y]==u;
a[x]=y,b[x]=_b[y],c[x]=_c[y];
if(flg) upd(x);
}
else res[i]+=query(x);
}
}
}
using block1::upd;
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++) _a[i]=read();
for(int i=1;i<=n;i++) _b[i]=read();
for(int i=1;i<=n;i++) _c[i]=read();
for(int i=1;i<=n;i++)
{
a[i]=_a[i],b[i]=_b[a[i]],c[i]=_c[a[i]];
pos[a[i]].push_back(i),pos[b[i]].push_back(i),pos[c[i]].push_back(i);
}
for(int i=1;i<=m;i++)
{
q[i].op=read(),q[i].x=read();
if(q[i].op==1)
{
q[i].y=read();
int x=q[i].x,y=q[i].y;
pos[y].push_back(x),pos[_b[y]].push_back(x),pos[_c[y]].push_back(x);
}
}
for(int i=1;i<=n;i++) bel[i]=(i-1)/B+1;
for(int i=1;i<=bel[n];i++) st[i]=(i-1)*B+1,ed[i]=min(i*B,n);
for(int i=1;i<=n;i++)
{
auto &vec=pos[i];
vec.push_back(0),sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
upd(i,1);
}
for(int i=1;i<=m;i++)
{
int x=q[i].x,y=q[i].y;
if(q[i].op==1)
{
set<int> tmp={a[x],b[x],c[x],y,_b[y],_c[y]};
for(auto x:tmp) upd(x,-1);
a[x]=y,b[x]=_b[y],c[x]=_c[y];
for(auto x:tmp) upd(x,1);
}
else res[i]=block1::query(x);
}
for(int i=1;i<=n;i++) block2::solve(i);
for(int i=1;i<=m;i++) if(q[i].op==2) printf("%lld\n",res[i]);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17535940.html
浙公网安备 33010602011771号