[Ynoi2007] tmpq 题解
为方便表述,下文令 \(S_{1,i}=b_{a_i},S_{2,i}=a_i,S_{3,i}=c_{a_i}\)。
由于要求三个数相等,所以每种数字是互不影响的,可以分开计算。
考虑 DP。设当前计算的数字为 \(x\),令 \(f_{i,j}\) 表示前 \(i\) 个数中选 \(j\) 个数放进三元组的方案数,有:
直接对每个询问暴力计算是 \(O(n^2m)\) 的。
考虑根号分治。令数字 \(x\) 被修改的次数为 \(cnt_x\),按 \(cnt_x\) 是否 \(\le\sqrt n\) 分类。
当 \(cnt_x\le\sqrt n\) 时,考虑在修改时暴力重构 DP 数组。如果 \(S_{1,i},S_{2,i},S_{3,i}\) 都不等于 \(x\),那么可以跳过这个 \(i\) 不去计算。
但这样查询就会出问题,可能出现算重等情况。
由于我们要统计的是 \(f_{r,3}\),代入上式展开,得:
因此可以用数据结构对每个 \(r\) 维护 \(\sum_x f_{r,3}\)。
重构 DP 数组时,在数据结构上修改的总次数是 \(O(n\sqrt n)\) 的,而查询的次数是 \(O(n)\) 的。因此考虑分块,\(O(1)\) 单点修改, \(O(\sqrt n)\) 区间求和。
当 \(cnt_x>\sqrt n\) 时,这样的 \(x\) 不超过 \(\sqrt n\) 个。此时考虑 DDP。
容易写出转移矩阵:
因此可以用数据结构维护矩阵。
修改次数 \(O(n)\),查询时枚举每一个 \(x\),查询次数是 \(O(n\sqrt n)\) 的。因此还是考虑分块,\(O(\sqrt n)\) 单点修改,\(O(1)\) 区间矩阵乘法。
按照上述思路写出一份代码并不难,但是这是 Ynoi。所以需要优化。
本题空间限制为 \(64\) MB,因此不能对每个 \(cnt_x>\sqrt n\) 的 \(x\) 开一个分块。由于前面提到每种数字是互不影响的,因此可以对每一块分别处理每个询问。
注意处理修改时,如果这个位置上 \(x\) 的出现情况没有任何变化,那就不要动它,否则复杂度会退化到 \(O(nm)\)。
DDP 的矩阵乘法常数为 \(4^3=64\),需要优化。观察转移矩阵,发现它是一个上三角矩阵,并且主对角线上都是 \(1\)。因此只需要存储右上方的 \(6\) 个数,做矩阵乘法时只需要对它们进行计算。
分块和根号分治的块长取 \(1000\) 比较合适。
#include <iostream>
#include <algorithm>
#include <vector>
#define ll long long
const int N=2e5+5,Q=5e4+5,B=1000,S=1000,M=205;
int n,m,a[N],b[N],c[N],ta[N],id[N],col[M],li[N][3],cnt[N];
struct Opt {int op,x,y;} q[Q];
ll f[4],ans[Q];
struct Node
{
int x,y;
bool operator <(const Node &g) const {return x!=g.x?x<g.x:y>g.y;}
};
std::vector<Node> buc[N];
struct Mat
{
ll f01,f02,f03,f12,f13,f23;
Mat() {f01=f02=f03=f12=f13=f23=0;}
Mat operator *(const Mat &g) const
{
Mat ret;
ret.f01=g.f01+f01;
ret.f02=g.f02+f01*g.f12+f02;
ret.f03=g.f03+f01*g.f13+f02*g.f23+f03;
ret.f12=g.f12+f12;
ret.f13=g.f13+f12*g.f23+f13;
ret.f23=g.f23+f23;
return ret;
}
};
namespace BS
{
Mat s[N],sum[M],pre[N];
int cf;
void upd(int i)
{
s[i].f01=li[i][0]==cf;
s[i].f12=li[i][1]==cf;
s[i].f23=li[i][2]==cf;
}
void rebuild(int x)
{
int lb=x*B,rb=std::min((x+1)*B-1,n);
pre[lb]=s[lb];
for(int i=lb+1;i<=rb;i++) pre[i]=pre[i-1]*s[i];
}
void build()
{
for(int i=1;i<=n;i++) upd(i);
for(int i=0;i<=n/B;i++) rebuild(i);
for(int i=0;i<n/B;i++) sum[i]=pre[(i+1)*B-1];
for(int i=1;i<n/B;i++) sum[i]=sum[i-1]*sum[i];
}
void md(int x)
{
upd(x);
int lc=x/B;
if(x==lc*B) rebuild(lc);
else
{
int rb=std::min((lc+1)*B-1,n);
for(int i=x;i<=rb;i++) pre[i]=pre[i-1]*s[i];
}
for(int i=lc;i<n/B;i++)
{
sum[i]=pre[(i+1)*B-1];
if(i) sum[i]=sum[i-1]*sum[i];
}
}
ll qr(int r) {return (r/B?sum[r/B-1]*pre[r]:pre[r]).f03;}
};
namespace BB
{
ll v[N],sum[M];
void ins(int t,int op) {sum[t/B]+=op*v[t];}
void md(int t,ll k) {v[t]+=k,sum[t/B]+=k;}
ll qr(int t)
{
ll ret=0;
for(int i=0;i<t/B;i++) ret+=sum[i];
for(int i=t/B*B;i<=t;i++) ret+=v[i];
return ret;
}
};
void md1(int x)
{
f[0]=1,f[1]=f[2]=f[3]=0;
for(Node i:buc[x])
{
f[i.y]+=f[i.y-1];
if(i.y==3)
BB::sum[i.x/B]-=BB::v[i.x],
BB::v[i.x]=f[2],
BB::sum[i.x/B]+=BB::v[i.x];
}
}
void ins(int x,Node t)
{
if(id[x]) return;
auto &g=buc[x];
g.emplace(std::lower_bound(g.begin(),g.end(),t),t);
md1(x);
}
void del(int x,Node t)
{
if(id[x]) return;
auto &g=buc[x];
auto it=std::lower_bound(g.begin(),g.end(),t);
if(t.y==3) BB::sum[it->x/B]-=BB::v[it->x],BB::v[it->x]=0;
g.erase(it),md1(x);
}
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>m;
for(int i=1;i<=n;i++) std::cin>>a[i],ta[i]=a[i];
for(int i=1;i<=n;i++) std::cin>>b[i];
for(int i=1;i<=n;i++) std::cin>>c[i];
for(int i=1;i<=n;i++)
cnt[a[i]]++,cnt[b[a[i]]]++,cnt[c[a[i]]]++,
li[i][0]=b[a[i]],li[i][1]=a[i],li[i][2]=c[a[i]];
for(int i=1,op,x,y;i<=m;i++)
{
std::cin>>op>>x;
if(op==1) std::cin>>y,cnt[y]++,cnt[b[y]]++,cnt[c[y]]++;
q[i]={op,x,y};
}
int len=0;
for(int i=1;i<=n;i++) if(cnt[i]>S) col[id[i]=++len]=i;
for(int i=1;i<=n;i++) ins(a[i],{i,2}),ins(b[a[i]],{i,1}),ins(c[a[i]],{i,3});
for(int i=1,op,x,y;i<=m;i++)
{
op=q[i].op,x=q[i].x,y=q[i].y;
if(op==1&&a[x]!=y)
{
li[x][0]=b[y],li[x][1]=y,li[x][2]=c[y];
del(b[a[x]],{x,1}),del(a[x],{x,2}),del(c[a[x]],{x,3});
ins(b[y],{x,1}),ins(y,{x,2}),ins(c[y],{x,3});
a[x]=y;
}
if(op==2) ans[i]+=BB::qr(x);
}
for(int w=1;w<=len;w++)
{
for(int i=1;i<=n;i++) a[i]=ta[i],li[i][0]=b[a[i]],li[i][1]=a[i],li[i][2]=c[a[i]];
BS::cf=col[w],BS::build();
for(int i=1,op,x,y;i<=m;i++)
{
op=q[i].op,x=q[i].x,y=q[i].y;
if(op==1&&a[x]!=y)
{
li[x][0]=b[y],li[x][1]=y,li[x][2]=c[y];
if(b[a[x]]==col[w]||a[x]==col[w]||c[a[x]]==col[w]||
b[y]==col[w]||y==col[w]||c[y]==col[w]) BS::md(x);
a[x]=y;
}
if(op==2) ans[i]+=BS::qr(x);
}
}
for(int i=1;i<=m;i++) if(q[i].op==2) std::cout<<ans[i]<<'\n';
}
调了一整天。

浙公网安备 33010602011771号