CF1408

CF1408 - Clusterization Counting

题目大意

给定(n)个点无向带权完全图,求将这些点分组,使得 组内的边边权 都小于 组内点连到组外点的边权

保证边权不同


分析

考虑如何确定合法的分组

从小到大依次加入每一条边,则一个合法的分组一定在某一个时刻满足

1.这个分组是一个极大的连通块

2.这个分组是一个团


dp

考虑类似( ext{Kruskal})重构树的方法,对于合并的过程转化为树形结构

此时,分组决策只有两种

1.这个点儿子内部分别分组,背包合并

2.如果这个点恰好是合法的分组,那么选择这个点建立新的分组,并且此时儿子必须都是散点

借用树形背包的复杂度分析,(dp)部分复杂度为(O(n^2))

排序可以桶排


const int N=3010,P=998244353;

int n,m,k;
int F[N],S[N],C[N];
int Find(int x){ 
	return F[x]==x?x:F[x]=Find(F[x]); 
}
int dp[N][N/2];
int X[N*N/4],Y[N*N/4];

int main(){
	n=rd();
	rep(i,1,n) F[i]=i,S[i]=1,C[i]=0,dp[i][0]=dp[i][1]=1;
	rep(i,1,n) rep(j,1,n) {
		int x=rd();
		if(i<j) X[x]=i,Y[x]=j;
	}
	rep(i,0,n*n) if(X[i]) {
		int x=Find(X[i]),y=Find(Y[i]);
		if(x==y) {
			if(++C[x]==S[x]*(S[x]-1)/2) dp[x][1]++;
		} else {
			++n;
			rep(a,0,S[x]) rep(b,0,S[y]) if((a>0)==(b>0)) dp[n][a+b]=(dp[n][a+b]+1ll*dp[x][a]*dp[y][b])%P;
			F[x]=F[y]=n,S[n]=S[x]+S[y],C[n]=C[x]+C[y]+1,F[n]=n;
			if(C[n]==S[n]*(S[n]-1)/2) dp[n][1]++;
		}
	}
	rep(i,1,S[n]) printf("%d ",dp[n][i]);
}
原文地址:https://www.cnblogs.com/chasedeath/p/14744458.html