[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\) 个数放进三元组的方案数,有:

\[f_{i,j}=f_{i-1,j}+f_{i-1,j-1}[S_{j,i}=x] \]

直接对每个询问暴力计算是 \(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}\),代入上式展开,得:

\[\begin{align*} f_{r,3}&=f_{r-1,3}+f_{r-1,2}[S_{2,r}=x]\\ &=\sum_{i\le r}f_{i-1,2}[S_{2,i}=x] \end{align*} \]

因此可以用数据结构对每个 \(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。

容易写出转移矩阵:

\[\large\begin{bmatrix} 1&[S_{1,i}=x]&0&0\\ 0&1&[S_{2,i}=x]&0\\ 0&0&1&[S_{3,i}=x]\\ 0&0&0&1 \end{bmatrix} \]

因此可以用数据结构维护矩阵。

修改次数 \(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';
}

调了一整天。

posted @ 2025-09-06 23:15  整齐的艾萨克  阅读(8)  评论(0)    收藏  举报