acm- (完全子图、团、暴力)Codeforces Round #684 (Div. 2) D. Graph Subset Problem

题面
传送门
首先,如果存在度数小于 k k k的点,如果这个点的度数为 k − 1 k-1 k1,那么它有机会成为一个大小为 k k k的完全子图的一个顶点,于是我们不妨暴力遍历跟这个点相连的所有点,如果这些点的对应的边集的另一个顶点都能找到彼此,那么就找到了完全子图了。可以用 b i n a r y _ s e a r c h binary\_search binary_search函数快速查找某个元素是否存在于某个有序数组中。如果发现不满足条件,那么这个点肯定不会成为大小为 k k k的完全子图的某一个顶点,因此可以将这个点以及它连接的边从整个图中删除。

度数小于 k k k的其它点,既不能成为完全子图,也不能成为有 k k k个邻边的点,也可以删掉。

最后删完了这些不合法的点后,我们就得到了一张度数都大于等于 k k k的图,当然可能这张图上一个点都没有,根据最后的情况来决定如何输出答案即可。

这里分析一下复杂度,对于删边这个操作复杂度是 O ( m ) O(m) O(m)的,可以忽略不计,主要看完全子图的判断这一部分,首先每次判断完全子图对应着会删除 k k k条边,故判断次数总共为 O ( m k ) O(\frac mk) O(km)次,然后每次判断完全子图都是 O ( k 2 l o g n ) O(k^2log n) O(k2logn)的,故总复杂度为 O ( m k l o g ( n ) ) O(mklog(n)) O(mklog(n)),注意完全子图保证了每个点度数为 k − 1 k-1 k1,事实上 m a x ( k ) = m max(k)=\sqrt m max(k)=m ,故总复杂度为 O ( m m l o g ( n ) ) O(m\sqrt mlog(n)) O(mm log(n)),能够通过本题。

int deg[maxn],vis[maxn];
vector<int>g[maxn],c;
queue<int>q;
int main(){
	int t=rd();
	while(t--){
		int n=rd(),m=rd(),k=rd();
		FOR(i,0,m){
			int u=rd(),v=rd();
			g[u].push_back(v);
			g[v].push_back(u);
		}
		FOR(i,1,n+1){
			sort(g[i].begin(),g[i].end());
			deg[i]=(int)g[i].size();
			if(deg[i]<k)q.push(i),vis[i]=1;
		} 
		while(!q.empty()){
			int u=q.front();q.pop();
			vis[u]=2;
			if(deg[u]==k-1 && c.empty()){
				bool ok=true; 
				c.push_back(u);
				FOR(i,0,g[u].size()){
					int v=g[u][i];
					if(vis[v]==2)continue;
					if(deg[v]<k-1){
						ok=false;
						break;
					}
					c.push_back(v);
				}
				if(!ok)c.clear();
				FOR(i,0,c.size()){
					FOR(j,0,c.size()){
						if(i==j)continue;
						int u=c[i],v=c[j];
						if(!binary_search(g[u].begin(),g[u].end(),v)){
							ok=false;
							break;
						}
					}
					if(!ok)break; 
				}
				if(!ok)c.clear();
			}
			FOR(i,0,g[u].size()){
				int v=g[u][i];
				if(vis[v]>=2)continue;
				deg[v]--;
				if(deg[v]<k && !vis[v])q.push(v),vis[v]=1;
			}
			deg[u]=0;
		}
		int sz=0;
		FOR(i,1,n+1)if(deg[i]){
			sz++;
		}
		if(sz){
			printf("1 %d\n",sz);
			FOR(i,1,n+1)if(deg[i])printf("%d ",i);puts("");
		}else if(!c.empty()){
			printf("2\n");
			FOR(i,0,c.size())printf("%d ",c[i]);puts("");
		}else wrn(-1);
		c.clear();
		FOR(i,1,n+1)g[i].clear(),deg[i]=0,vis[i]=0;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值