#弦图,并查集,扩展KMP#洛谷 15549 「Stoi2037」不能说的秘密

题目传送门


分析

由于存在 \(p_i=m\),那么 \(S[i,i+m-1]=T[1,m],z_j=\min\{p_{i+j-1},m-i+1\}\)。

然后根据扩展 KMP 可以发现 \(S[i],T[j]\) 以及 \(T[i],T[j]\) 存在相等或不等的关系,用并查集合并相等关系后,

剩下相当于 \(c-\)染色,而构造出来的图其实是弦图,弦图染色方案数是 \(\prod (c-label[x])\),跑 MCS 求出 label 即可。

特别地,如果不存在 \(p_i=m\),那么 \(T_{\max\{p_i\}+1}\) 也存在不等关系,

此时图不一定是弦图,虽然后缀能任选,但也许得在弦图的消除序列上dp。


代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N=2000011; struct node{int y,next;}e[N<<3]; vector<int>K[N]; pair<int,int>b[N];
int T,n,m,k,et,as[N],label[N],peo[N],v[N],tag[N],p[N],z[N],f[N],flag,ans,tot;
int getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
void Get_Z(int m){
	z[1]=m;
	for (int i=2,l=0,r=0;i<=m;++i){
		int now=0;
		if (i<=r){
			if (z[i-l+1]<r-i+1){
				if (z[i]!=z[i-l+1]){
					flag=0;
					continue;
				}
				now=z[i];
			}else{
				if (z[i]<r-i+1){
					flag=0;
					continue;
				}
				now=r-i+1;
			}
		}
		for (int j=now;j<z[i];++j){
			int fa=getf(n+j+i),fb=getf(n+j+1);
			if (fa>fb) swap(fa,fb);
			if (fa!=fb) f[fa]=fb;
		}
		if (i+z[i]-1>r) l=i,r=i+z[i]-1;
		if (i+z[i]<=m) b[++tot]=make_pair(n+z[i]+i,n+z[i]+1);
	}
}
void ExKMP(int n){
	for (int i=1,l=0,r=0;i<=n;++i){
		int now=0;
		if (i<=r){
			if (z[i-l+1]<r-i+1){
				if (p[i]!=z[i-l+1]){
					flag=0;
					continue;
				}
				now=p[i];
			}else{
				if (p[i]<r-i+1){
					flag=0;
					continue;
				}
				now=r-i+1;
			}
		}
		for (int j=now;j<p[i];++j){
			int fa=getf(j+i),fb=getf(n+j+1);
			if (fa>fb) swap(fa,fb);
			if (fa!=fb) f[fa]=fb;
		}
		if (i+p[i]-1>r) l=i,r=i+p[i]-1;
		if (i+p[i]<=n&&p[i]<m) b[++tot]=make_pair(p[i]+i,n+p[i]+1);
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	for (cin>>T;T;--T){
		cin>>n>>m>>k,et=flag=ans=1,tot=0;
		for (int i=1;i<=n;++i){
			cin>>p[i];
			if (i+p[i]-1>n) flag=0;
			if (p[i]==m) flag=i;
		}
		if (!flag){
			cout<<0<<'\n';
			continue;
		}
		for (int i=1;i<=m;++i) z[i]=min(p[flag+i-1],m-i+1);
		for (int i=0;i<=n+m;++i) f[i]=i;
		flag=1,Get_Z(m),ExKMP(n);
		for (int i=1;i<=tot;++i){
			int x=getf(b[i].first),y=getf(b[i].second);
			if (x==y){
				flag=0;
				break;
			}
		}
		if (!flag){
			cout<<0<<'\n';
			continue;
		}
		for (int i=1;i<=tot;++i){
			int x=getf(b[i].first),y=getf(b[i].second);
			e[++et]=(node){y,as[x]},as[x]=et;
			e[++et]=(node){x,as[y]},as[y]=et;
		}
		tot=0;
		for (int i=1;i<=n+m;++i)
			if (getf(i)==i) K[0].push_back(i),++tot;
		for (int i=tot,mx=0;i;--i){
			int x=-1;
			while (x==-1){
				while (K[mx].empty()) --mx;
				x=K[mx].back(),K[mx].pop_back();
				if (v[x]||label[x]!=mx) {x=-1; continue;}
			}
			v[x]=1,peo[i]=x;
			if (label[x]>=k) ans=0;
				else ans=1ll*ans*(k-label[x])%173127047;
			for (int j=as[x];j;j=e[j].next)
			if (!v[e[j].y]&&tag[e[j].y]!=x){
				tag[e[j].y]=x;
				if (++label[e[j].y]>mx) mx=label[e[j].y];
				K[label[e[j].y]].push_back(e[j].y);
			}
		}
		cout<<ans<<'\n';
		for (int i=0;i<=n+m;++i) as[i]=label[i]=tag[i]=peo[i]=v[i]=0,K[i].clear();
	}
	return 0;
}
posted @ 2026-09-27 19:55  lemondinosaur  阅读(6)  评论(0)    收藏  举报