题解 P3209 【[HNOI2010]PLANAR】
其实不用2-sat的,直接并查集。
先用平面图定理 m<=3*n+6 把边数减小到O(n)级别。
然后设 集合 i+m 表示 不能与i共存的边 所在的集合。
枚举每一对不能共存的边,如果已经在同一个集合内,则无解。
否则交叉连边 fa[find(i)]=j+m , fa[find(j)]=i+m 。
如何判断两条边是否相交?先把所有边坐标转换为哈密顿回路中的顺序坐标,对于任意两条边i,j,如果有xi<xj<yi<yj,则相交。
注意如果两条边任意不在同一条边上的两端点相同则不可能相交。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2e3+1e1;
int x[maxn<<4],y[maxn<<4],vis[maxn<<4];
int id[maxn];
int fa[maxn<<5];
int T,n,m;
inline int findfa(int x)
{
return fa[x] == x ? x : fa[x] = findfa(fa[x]);
}
inline void initfa()
{
for(int i=1;i<=m<<1;i++)
fa[i] = i;
}
inline bool cross(int x1,int x2,int y1,int y2)
{
if( x1 == x2 || y1 == y2 || x1 == y2 || x2 == y1 )
return 0;
if( x1 < x2 && y1 < y2 && x2 < y1 )
return 1;
if( x2 < x1 && y2 < y1 && x1 < y2 )
return 1;
return 0;
}
inline bool check()
{
initfa();
for(int i=1;i<=m;i++)
{
if( vis[i] )
continue;
for(int j=1;j<=m;j++)
{
if( vis[j] )
continue;
if( !cross(x[i],x[j],y[i],y[j]) )
continue;
int fai = findfa(i) , faj = findfa(j);
if( fai == faj )
return 0;
fa[fai] = findfa( j + m ),
fa[faj] = findfa( i + m );
}
}
return 1;
}
inline void init()
{
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
memset(vis,0,sizeof(vis));
n = m = 0;
}
inline int getint()
{
int ret = 0;
char ch = getchar();
while( ch < '0' || ch > '9' )
ch = getchar();
while( '0' <= ch && ch <= '9' )
ret = ret * 10 + ( ch - '0' ),
ch = getchar();
return ret;
}
int main()
{
T = getint();
while( T-- )
{
init();
n = getint() , m = getint();
for(int i=1;i<=m;i++)
x[i] = getint() , y[i] = getint();
for(int i=1;i<=n;i++)
id[getint()] = i;
if( m > 3 * n + 6 )
{
puts("NO");
continue;
}
for(int i=1,a,b;i<=m;i++)
{
a = id[x[i]] , b = id[y[i]];
x[i] = min( a , b ),
y[i] = max( a , b );
}
for(int i=1;i<=m;i++)
if( y[i] == x[i] + 1 || ( y[i]==n && x[i]==0) )
vis[i] = 1;
if( check() )
puts("YES");
else puts("NO");
}
return 0;
}