zoukankan      html  css  js  c++  java
  • UOJ632. 【UR #21】挑战最大团

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

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

    (nle 8000)


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

    结论2:优美的图的补图一定是优美的。证明:如果不优美,那么补图中存在(a,b,c,d)使得((a,b),(b,c),(c,d)in E)并且((a,c),(b,d),(a,d) otin 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=Sigcup T)联通所以一定存在(xin S,yin 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;
    }
    
  • 相关阅读:
    postgresql删除活动链接的数据库
    第四篇 函数
    Jmeter响应中文乱码解决办法
    第三篇 条件控制和循环
    第二篇 Python运算符
    npm更换为镜像
    第一篇 Python的数据类型
    newman的常用命令使用总结
    windows下安装newman
    同态包络提取
  • 原文地址:https://www.cnblogs.com/jz-597/p/14833630.html
Copyright © 2011-2022 走看看