[ACM]【分治/三元环/暴力】牛客练习赛62 牛妹的游戏

牛妹的游戏

传送门
题意:给n个点m条边的图,求有无三元环,若无,求补图有无三元环。
在这里插入图片描述

思路

正解:
证明出如果n大于等于6,那么一定输出yes。拉姆塞理论

小于6的部分暴力。
证明:
对于一个点数大于6的图的任意一个点,一定存在另外至少三个点与它的连线能属于同一阵营。(显然,如果n<=5就会有一个点1与另外23的连线属于一个阵营,剩下与45的连线属于另一个阵营的情况。)那么对于1,假若他与2、3、4的连线都属于蓝方,那么只要存在2、3、4中任意两者之间有蓝方的连线,就能构成三角型。如果2、3、4任意之间都是绿方的连线,也能构成一个三角型。因此一定存在一方有三角型。

#include<bits/stdc++.h>
using namespace std;
int a[10][10];
inline int read(){
	int f=1,x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
int main(){
	int t=read();
	while(t--){
		int n=read(),m=read();
		if(n>=6){
			for(int i=1;i<=2*m;i++) int a=read();
			printf("yes\n");
			continue;
		}
		memset(a,0,sizeof(a));
		for(int i=1;i<=m;i++){
			int u=read(),v=read();
			a[u][v]=1;
			a[v][u]=1;
		}
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				for(int k=j+1;k<=n;k++){
					int sum=a[i][j]+a[j][k]+a[i][k];
					if(sum==0||sum==3)goto yes;
				}
			}
		}
		printf("no\n");
		continue;
		yes:
			printf("yes\n");
	}
}

假解:
暴力枚举+善用STL+随机。
虽是暴力,但运用了分治来做,度数大于根号m的枚举点,小于根号m的枚举相邻边。之所以为什么用根号m来分,我也不造啊( ╯□╰ ),明白了再来补。三元环的分治操作
判断有无连接使用了二维的map的count函数。
接下来判断补图有无三元环。今早补题借鉴了大佬的题解的随机抽取(其实这个操作不太合理,这也是这个作法成为假解的原因…)

#include<bits/stdc++.h>
using namespace std;
const int maxn=50004;
int vis[maxn],in[maxn];	//vis判重剪枝 in记录入度数目
map<int,int>p[maxn]; //记录直接相连的关系,并使用了found(直接数组存可能会MLE吧
vector<int>g[maxn]; //记录每个点的度
vector<int>low,high; //记录分治的两种点
inline int read(){ //快读
	int f=1,x=0;
	char ch;
	ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int main(){
	srand(time(NULL));
	int t;
	t=read();
	while(t--){
		int n=read(),m=read(),ans=0;
		//一连串的清空操作
		vector<int>().swap(low);
		vector<int>().swap(high);
		memset(vis,0,sizeof(vis));
		memset(in,0,sizeof(in));
		for(int i=1;i<=n;i++){
			p[i].clear();
			vector<int>().swap(g[i]);
		}
		for(int i=1;i<=m;i++){
			int u=read(),v=read();
			g[u].push_back(v);
			g[v].push_back(u);
			p[u][v]=1,p[v][u]=1;
			in[u]++,in[v]++;
		}
		for(int i=1;i<=n;i++)
			if(in[i]<=sqrt(m)) low.push_back(i);
			else high.push_back(i);
		//处理小度数的点
		for(int i=0;i<low.size();i++){
			int x=low[i];
			vis[x]=1;
			for(int j=0;j<g[x].size();j++){
				int y=g[x][j];
				if(vis[y]) continue;
				for(int k=j+1;k<g[x].size();k++){
					int z=g[x][k];
					if(!vis[z]&&p[y].count(z)) goto yes;
				}
			}
		}
		//处理大度数的点
		for(int i=0;i<high.size();i++){
			int x=high[i];
			for(int j=i+1;j<high.size();j++){
				int y=high[j];
				if(!p[x].count(y)) continue;
				for(int k=j+1;k<high.size();k++){
					int z=high[k];
					if(p[y].count(z)&&p[x].count(z)) goto yes;
				}
			}
		}
		//随机(太玄了)
		for(int t=1,x,y,z;t<=500000;t++){
			x=rand()%n+1,y=rand()%n+1,z=rand()%n+1;
			if(x==y||y==z||x==z) continue;
			if(!p[x].count(y)&&!p[y].count(z)&&!p[x].count(z)) goto yes;
		}
		printf("no\n");
		continue;
		yes:
			printf("yes\n");
	}
}
发布了20 篇原创文章 · 获赞 1 · 访问量 576

猜你喜欢

转载自blog.csdn.net/weixin_45497996/article/details/105746047