CF1408G 题解

CF1408G

模拟赛 T2。

思路

从小到大加边,用并查集维护,记录当前联通块的点数 \(siz_i\) 和边数 \(num_i\)。如果 \(num_i=\frac{siz_i\times (siz_i-1)}{2}\) 说明当前联通块是一个合法的组。但是不好计数。

手玩发现,对于任意两个合法的组,他们要么相互包含要么相互不交。这说明我们可以自己给点定一个顺序使得一个组重排后是一个区间。这样设 \(dp_{i,j}\) 表示重新排序后前 \(i\) 个点有 \(j\) 个组的方案数,把一段合法的区间放在最左边的点上,可以 dp。

先做一遍并查集,记录 \(n-1\) 次合并前 \(f_u\) 的值。从后往前遍历每次合并 \(f_v=u\),将合并后所有 \(f_x=u\)\(x\) 按合并前 \(f_x=u\)\(f_x=v\) 将分为左右两边。再用新的 \(id_u\) 做并查集,找到的合法的组都是区间。

可以合理相信合法的组的数量不多,小于 \(n^2\)。复杂度 \(O(n^2)\)

code

int n,a[maxn][maxn];
int f[maxn],siz[maxn],num[maxn];
int fd(int x){
	if(f[x]==x)return x;
	return f[x]=fd(f[x]);
}
pii pos[maxn*maxn];
int lst[maxn][maxn],tp;
pii st[maxn];
int id[maxn],tmp[maxn],rnk[maxn];
vector<int> p[maxn];
int dp[maxn][maxn];
void work(){
	n=read();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)a[i][j]=read();
		a[i][i]=inf;
	}
	for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)pos[a[i][j]]={i,j};
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<=n*(n-1)/2;i++){
		int u=pos[i].first,v=pos[i].second;
		u=fd(u),v=fd(v);
		if(u!=v){
			st[++tp]={v,u};
			for(int j=1;j<=n;j++)lst[tp][j]=fd(j);
			f[v]=u;
		}
	}
	++tp;for(int j=1;j<=n;j++)lst[tp][j]=fd(j);
	for(int i=1;i<=n;i++)id[i]=i;
	for(int i=tp-1;i;i--){
		for(int j=1;j<=n;j++)tmp[j]=id[j];
		int l,r;
		// for(int j=1;j<=n;j++)cout<<id[j]<<" ";cout<<"\n";
		for(int j=1;j<=n;j++)if(lst[i+1][tmp[j]]==st[i].second){l=j;break;}
		for(int j=1;j<=n;j++)if(lst[i+1][tmp[j]]==st[i].second)r=j;
		// cout<<i<<" "<<st[i].first<<" "<<st[i].second<<" "<<l<<" "<<r<<"\n";
		int h=l;
		for(int j=l;j<=r;j++)if(lst[i][tmp[j]]==st[i].second)id[h++]=tmp[j];
		for(int j=l;j<=r;j++)if(lst[i][tmp[j]]==st[i].first)id[h++]=tmp[j];
	}
	// for(int i=1;i<=n;i++)cout<<id[i]<<" ";cout<<"\n";
	for(int i=1;i<=n;i++)rnk[id[i]]=i;
	for(int i=1;i<=n;i++)f[i]=i,siz[i]=1,num[i]=0,p[i].push_back(i);
	for(int i=1;i<=n*(n-1)/2;i++){
		int u=rnk[pos[i].first],v=rnk[pos[i].second];
		if(u>v)swap(u,v);
		u=fd(u),v=fd(v);
		if(u==v)num[u]++;
		else{
			f[v]=u;siz[u]+=siz[v];num[u]+=num[v]+1;
		}
		if(num[u]==siz[u]*(siz[u]-1)/2){
			// for(int j=1;j<=n;j++)cout<<fd(j)<<" ";cout<<"\n";
			// for(int j=u;j<=n+1;j++)if(fd(j)!=u){
				// p[u].push_back(j-1);
				// // cout<<u<<" "<<j-1<<"\n";
				// break;
			// }
			p[u].push_back(u+siz[u]-1);
		}
	}
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)if(dp[i-1][j-1]){
			// cout<<i<<" "<<j<<" "<<dp[i-1][j-1]<<"\n";
			for(int k:p[i])(dp[k][j]+=dp[i-1][j-1])%=mod;
		}
	}
	for(int i=1;i<=n;i++)printf("%lld ",dp[n][i]);
}
posted @ 2024-05-10 19:53  yhddd  阅读(18)  评论(0)    收藏  举报