一道比较奇怪的题
用二分图染色和2-SAT都可以建模
想了一下为什么
二分图染色是找出矛盾的关系连边
2-SAT是找出“必须”的关系连边
而此题恰好都满足
--------------------------------------------------
2-SAT做法:
一组边a,b在环构成的圆内相交 那么在圆外的a‘,b'也相交
”必须“关系有 a-->b' a'-->b b-->a‘ b'-->a
直接连边就可以了然后tarjan判断
--------------------------------------------------
二分图染色做法:
引用上面的a,b,a',b'
矛盾关系就是 a-->a' b-->b' a-->b a'-->b'
两种算法的复杂度是一样的然后用二分图染色的话,常数会稍小点
下面贴代码
2-SAT 124ms
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#include<iostream>
using namespace std;
int sc()
{
int i=0; char c=getchar();
while( c>'9' || c<'0' ) c=getchar();
while( c>='0'&& c<='9') i=i*10+c-'0',c=getchar();
return i;
}
int u[11111],v[11111];
int head[1888],nxt[1222222],lst[1222222];
int dfn[1888],low[1888],st[1888],vis[1888],bl[1888];
int num[222],pos[222];
int n,m,total,tot,cnt,scc,top;
void insert(int x,int y)
{
lst[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void tarjan(int x)
{
vis[x]=1; st[++top]=x;
dfn[x]=low[x]=++cnt;
for(int i=head[x]; i; i=nxt[i])
{
if(vis[lst[i]]) low[x]=min(low[x],dfn[lst[i]]);
else if(!dfn[lst[i]])
{
tarjan(lst[i]);
low[x]=min(low[x],low[lst[i]]);
}
}
if(dfn[x]==low[x])
{
int k=0; scc++;
while(k!=x)
{
k=st[top--];
bl[k]=scc;
vis[k]=0;
}
}
}
int main()
{
int Q=sc();
while(Q--)
{
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
int flag=0; total=tot=cnt=top=scc=0;
n=sc(); m=sc();
for(int i=1; i<=m; i++) u[i]=sc(),v[i]=sc();
for(int i=1; i<=n; i++) num[i]=sc(),pos[num[i]]=i;
if(m>3*n-6) {puts("NO");continue;}
for(int i=1; i<=m; i++)
{
u[i]=pos[u[i]],v[i]=pos[v[i]];
if(u[i]>v[i]) swap(u[i],v[i]);
if(v[i]-u[i]==1||v[i]-u[i]==n-1) continue;
u[++total]=u[i],v[total]=v[i];
}
for(int i=1; i<=total; i++)
for(int j=i+1; j<=total; j++)
if((u[i]<u[j]&&u[j]<v[i]&&v[i]<v[j])||(u[j]<u[i]&&u[i]<v[j]&&v[j]<v[i]))
{
insert(2*i,2*j+1);
insert(2*i+1,2*j);
insert(2*j,2*i+1);
insert(2*j+1,2*i);
}
for(int i=2; i<=2*total+1; i++)
if(!dfn[i]) tarjan(i);
for(int i=1; i<=total; i++)
if(bl[i*2]==bl[i*2+1]){flag=1,puts("NO");break;}
if(!flag) puts("YES");
}
return 0;
}
二分图染色 100ms
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#include<iostream>
using namespace std;
int sc()
{
int i=0; char c=getchar();
while( c>'9' || c<'0' ) c=getchar();
while( c>='0'&& c<='9') i=i*10+c-'0',c=getchar();
return i;
}
int u[11111],v[11111];
int head[1888],nxt[1222222],lst[1222222];
int col[1888];
int num[222],pos[222];
int n,m,total,tot;
void insert(int x,int y)
{
lst[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
bool color(int x,int f)
{
col[x]=f;
for(int i=head[x]; i; i=nxt[i])
{
if(!col[lst[i]])
{
if(!color(lst[i],f^1)) return 0;
}
else if(col[lst[i]]==col[x]) return 0;
}
return 1;
}
int main()
{
int Q=sc();
while(Q--)
{
memset(head,0,sizeof(head));
memset(col,0,sizeof(col));
int flag=0; total=tot=0;
n=sc(); m=sc();
for(int i=1; i<=m; i++) u[i]=sc(),v[i]=sc();
for(int i=1; i<=n; i++) num[i]=sc(),pos[num[i]]=i;
if(m>3*n-6) {puts("NO");continue;}
for(int i=1; i<=m; i++)
{
u[i]=pos[u[i]],v[i]=pos[v[i]];
if(u[i]>v[i]) swap(u[i],v[i]);
if(v[i]-u[i]==1||v[i]-u[i]==n-1) continue;
u[++total]=u[i],v[total]=v[i];
}
for(int i=1; i<=total; i++)
for(int j=i+1; j<=total; j++)
if((u[i]<u[j]&&u[j]<v[i]&&v[i]<v[j])||(u[j]<u[i]&&u[i]<v[j]&&v[j]<v[i]))
{
insert(2*i,2*j);
insert(2*j,2*i);
insert(2*i+1,2*j+1);
insert(2*j+1,2*i+1);
}
for(int i=1; i<=total; i++) insert(2*i,2*i+1),insert(2*i+1,2*i);
for(int i=2; i<=total*2+1; i++)
if(!col[i]&&!color(i,2)){flag=1;break;}
if(flag)puts("NO");else puts("YES");
}
return 0;
}