[BZOJ1997][HNOI2010]Planar
题意
给你一张含有一条哈密顿回路的无向图(哈密顿回路就是经过每个店恰好一次),判断这张图是不是平面图(平面图就是可以画在平面上使边不相交)。
sol
首先有一个平面图定理:一张平面图的边数不超过\(3N-6\)(其中\(N\)是点数)
这样就把边数降到了\(O(n)\)级别。
把环抠出来,剩下的边就只能从环的里面连或者从环的外面连。
问题转化成\(2-sat\)。
直接\(O(n^2)\)建边即可。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 605;
const int M = 4e5+5;
struct edge{int u,v;}E[M];
int T,n,m,p[N],tot,to[M],nxt[M],head[N],cnt,dfn[N],low[N],tim,Stack[N],top,vis[N],bel[N],scc;
bool Cross(int l1,int r1,int l2,int r2)
{
if (l1>l2) swap(l1,l2),swap(r1,r2);
return l1<l2&&l2<r1&&r1<r2;
}
void link(int u,int v)
{
to[++cnt]=v;nxt[cnt]=head[u];
head[u]=cnt;
}
void Tarjan(int u)
{
dfn[u]=low[u]=++tim;
Stack[++top]=u;vis[u]=1;
for (int e=head[u];e;e=nxt[e])
if (!dfn[to[e]]) Tarjan(to[e]),low[u]=min(low[u],low[to[e]]);
else if (vis[u]) low[u]=min(low[u],dfn[to[e]]);
if (dfn[u]==low[u])
{
++scc;int v;
do{
v=Stack[top--];
vis[v]=0;bel[v]=scc;
}while (u!=v);
}
}
bool check()
{
for (int i=1;i<=tot;++i)
if (bel[i]==bel[i+tot]) return false;
return true;
}
int main()
{
T=gi();
while (T--)
{
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
cnt=tot=scc=tim=0;
n=gi();m=gi();
for (int i=1;i<=m;++i) E[i]=(edge){gi(),gi()};
for (int i=1;i<=n;++i) p[gi()]=i;
if (m>3*n-6) {puts("NO");continue;}
for (int i=1;i<=m;++i)
{
E[i].u=p[E[i].u];E[i].v=p[E[i].v];
if (E[i].u>E[i].v) swap(E[i].u,E[i].v);
if (E[i].u+1!=E[i].v&&!(E[i].u==1&&E[i].v==n)) E[++tot]=E[i];
}
for (int i=1;i<=tot;++i)
for (int j=i+1;j<=tot;++j)
if (Cross(E[i].u,E[i].v,E[j].u,E[j].v))
link(i,j+tot),link(j,i+tot),link(i+tot,j),link(j+tot,i);
for (int i=1;i<=(tot<<1);++i) if (!dfn[i]) Tarjan(i);
puts(check()?"YES":"NO");
}
return 0;
}