牛妹的游戏
传送门
题意:给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");
}
}