其他分享
首页 > 其他分享> > UOJ632. 【UR #21】挑战最大团

UOJ632. 【UR #21】挑战最大团

作者:互联网

优美的图定义为:对于任意存在边\((a,b),(b,c),(c,d)\)的四个点,\((a,c),(b,d),(a,d)\)不能同时不出现。

现在给出一个优美的图,要对\(k\in [1,n]\)算大小为\(k\)的团的个数。

\(n\le 8000\)


结论1:优美的图的子图一定是优美的。(显然)

结论2:优美的图的补图一定是优美的。证明:如果不优美,那么补图中存在\(a,b,c,d\)使得\((a,b),(b,c),(c,d)\in E\)并且\((a,c),(b,d),(a,d)\notin E\),则原图中\((a,c),(b,d),(a,d)\in E\)且\((a,b),(b,c),(c,d)\in E\),于是原图不优美,矛盾。

结论3:优美的图的原图和补图不能同时联通。

证明:考虑归纳。加入新点\(v\),假如之前的图\(G\)原图不连通,补图联通,假设现在原图联通,补图联通。\(v\)会向每个连通块\(C_i\)连至少一条边,且至少一个连通块有点没有和\(v\)直接相连,设这个连通块为\(C_1\)。在\(C_1\)中假如和\(v\)直接相连的点集合为\(S\),没有相连的点的集合为\(T\)。因为\(C_1=S\bigcup T\)联通所以一定存在\(x\in S,y\in T,(x,y)\in E\)。任取\(C_2\)中和\(v\)有连边的点\(z\)。于是此时有:\((z,v),(v,x),(x,y)\in E\),然而\((z,x),(v,y),(z,y)\)都没有出现。矛盾。

于是做法是:如果原图不连通,就按照原图连通块划分子问题,结果的生成函数加起来;如果补图不连通,就按照补图连通块划分子问题,结果的生成函数乘起来。(因为有不选的情况所以不是直接说的那样,非重点,不详细描述)

朴素的方法是先BFS判断连通性,然后在那个不连通的图中随便选个点然后BFS出个联通块,分出两个子问题做。暴力干时间\(O(n^3)\)。后面考虑如何快速实现这个过程。如果每次能划分成两个子问题\(X,Y\),花不超过\(O(|X||Y|)\)的时间,时间复杂度和树上背包是一样的\(O(n^2)\)。合并信息不用超过这个时间,现在考虑如何在这个时间内划分出子问题。

结论4:优美的图的直径不超过\(2\)(图的直径是两两之间距离的最大值,距离为两点间最短路径长度)。根据优美的图定义容易推导得出。

于是BFS只需要遍历两层。如果选出个度数为\(d\)的点,从它开始BFS,那么花的时间是\(O(dn)\)。在原图和补图中分别选出度数最小的点,遍历度数较小的,就可以花\(O(\min(d_0,d_1)n)\)时间得到连通性,接下来再用\(O(dn)\)跑出连通块。因为最小度数小于最小连通块大小,所以时间也不超过\(O(\min(|X|,|Y|)n)=O(|X||Y|)\)。

实现上注意要顺便维护度数,不要每次暴力算出来。


using namespace std;
#include <bits/stdc++.h>
#define N 8005
#define ll long long
#define mo 998244353
int n;
int e[N][N],deg[N];
void read(){
	scanf("%d",&n);
	static char str[N];
	for (int i=0;i<n;++i){
		scanf("%s",str);
		for (int j=0;j<n-i-1;++j)
			if (e[i][i+j+1]=e[i+j+1][i]=(isdigit(str[j/4])?str[j/4]-'0':str[j/4]-'A'+10)>>j%4&1)
				deg[i]++,deg[i+j+1]++;
	}
}
void add(int c[],int a[],int b[],int na,int nb){
	static int t[N];
	memset(t+1,0,sizeof(int)*(na+nb));
	for (int i=1;i<=na;++i) t[i]+=a[i];
	for (int i=1;i<=nb;++i) (t[i]+=b[i])%=mo;
	memcpy(c+1,t+1,sizeof(int)*(na+nb));
}
void multi(int c[],int a[],int b[],int na,int nb){
	static int t[N];
	add(t,a,b,na,nb);
	for (int i=1;i<=na;++i)
		for (int j=1;j<=nb;++j)
			t[i+j]=(t[i+j]+(ll)a[i]*b[j])%mo;
	memcpy(c+1,t+1,sizeof(int)*(na+nb));
}
int _q[N],_p[N];
void work(int[],int[],int);
bool find(int q[],int p[],int k,int r,int o){
	static int vis[N],BZ;
	vis[r]=++BZ;
	for (int i=0;i<k;++i)
		if (e[r][q[i]]==o){
			vis[q[i]]=BZ;
			for (int j=0;j<k;++j)
				if (e[q[i]][q[j]]==o)
					vis[q[j]]=BZ;
		}
	bool all=1;
	for (int i=0;i<k;++i)
		all&=(vis[q[i]]==BZ);
	if (all) return 0;
	static int t[N];
	int cl=0,cr=k;
	for (int i=0;i<k;++i)
		(vis[q[i]]==BZ?t[cl++]:t[--cr])=q[i];
	memcpy(q,t,sizeof(int)*k);
	for (int i=0;i<cl;++i)
		for (int j=cl;j<k;++j)
			if (e[q[i]][q[j]])
				deg[q[i]]--,deg[q[j]]--;
	int *pl=p,*pr=p+cl;
	work(q,pl,cl);
	work(q+cl,pr,k-cl);
	if (o==0)
		multi(p,pl,pr,cl,k-cl);
	else
		add(p,pl,pr,cl,k-cl);
	return 1;
}
void work(int q[],int p[],int k){
	if (k==1){
		p[1]=1;
		return;
	}
	int r[2]={-1,-1},dr[2]={n,n};
	for (int i=0;i<k;++i){
		int d=deg[q[i]];
		if (d<dr[1]) r[1]=q[i],dr[1]=d;
		if (k-1-d<dr[0]) r[0]=q[i],dr[0]=k-1-d;
	}
	int fir=(dr[0]<dr[1]?0:1);
	if (!find(q,p,k,r[fir],fir))
		find(q,p,k,r[fir^1],fir^1);
}
int main(){
	read();
	for (int i=0;i<n;++i)
		_q[i]=i;
	work(_q,_p,n);
	for (int i=1;i<=n;++i)
		printf("%d ",_p[i]);
	return 0;
}

标签:原图,连通,优美,UOJ632,int,UR,补图,联通,21
来源: https://www.cnblogs.com/jz-597/p/14833630.html