洛谷P3209 [HNOI2010]平面图判定(2-SAT)
看到哈密顿回路就被吓傻了……结果没有好好考虑性质……
首先,平面图有个性质:边数小于等于$3n-6$(我也不知道为啥),边数大于这个的直接pass
然后考虑原图,先把哈密顿回路单独摘出来,就是一个环。对于每一条不在哈密顿回路上的边,有两种可能,一种是在环内,一种是在环外
我们用点来表示每一条边,把每一个点拆成两个分别表示这条边是在环内还是环外。对于两条边$i,j$,如果他们同时在环外或环内会交叉,那么就相当于有了约束条件,转化成一个2-SAT问题即可
至于连边,我们设$i$表示在环内,$i+m$表示在环外,如果$i,j$同在环内或环外会相交,那么连边$(i,j+m),(i+m,j),(j,i+m),(j+m,i)$,即他们永远不能同时在环内或环外
至于如果判断是否会相交,我们可以把环拆开,然后判断同在一侧是否会相交即可
//minamoto
#include<iostream>
#include<cstdio>
#include<cstring>
#define mem(a) (memset(a,0,sizeof(a)))
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,:;}
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
const int N=2e4+,M=1e5+;
int head[N],Next[M],ver[M],tot;
inline void add(int u,int v){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
}
int dfn[N],low[N],bl[N],st[N],num,cnt,top,n,m,k;
void tarjan(int u){
dfn[u]=low[u]=++num,st[++top]=u;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!dfn[v]) tarjan(v),cmin(low[u],low[v]);
else if(!bl[v]) cmin(low[u],dfn[v]);
}
if(low[u]==dfn[u]) for(++cnt;st[top+]!=u;--top) bl[st[top]]=cnt;
}
inline bool check(){
for(int i=,l=m<<;i<=l;++i) if(!dfn[i]) tarjan(i);
for(int i=;i<=m;++i)
if(bl[i]==bl[i+m]) return false;
return true;
}
int rev[],cir[][],E[],eu[N],ev[N];
void clear(){
mem(head),mem(dfn),mem(low),mem(bl),mem(st),mem(cir);
num=cnt=top=tot=;
}
void solve(){
clear();
n=read(),m=read(),k=;
for(int i=;i<=m;++i){
eu[i]=read(),ev[i]=read();
if(eu[i]>ev[i]) swap(eu[i],ev[i]);
}
for(int i=;i<=n;++i){
rev[E[i]=read()]=i;
if(i>){
int u=E[i-],v=E[i];
u<v?cir[u][v]=:cir[v][u]=;
}
}
if(m>*n-) return (void)(puts("NO"));
for(int i=;i<=m;++i)
if(!cir[eu[i]][ev[i]]) eu[++k]=eu[i],ev[k]=ev[i];
m=k;
for(int i=;i<m;++i)
for(int j=i+;j<=m;++j){
int u=rev[eu[i]],v=rev[ev[i]],x=rev[eu[j]],y=rev[ev[j]];
if(u>v) swap(u,v);if(x>y) swap(x,y);
if((u<x&&v>x&&v<y)||(u>x&&u<y&&v>y)){
add(i,j+m),add(j,i+m),add(i+m,j),add(j+m,i);
}
}
puts(check()?"YES":"NO");
}
int main(){
// freopen("testdata.in","r",stdin);
int T=read();
while(T--) solve();
return ;
}
洛谷P3209 [HNOI2010]平面图判定(2-SAT)的更多相关文章
- 洛谷 P3209 [HNOI2010] 平面图判定
链接: P3209 题意: 给出 \(T\) 张无向图 \((T\leq100)\),并给出它对应的哈密顿回路,判断每张图是否是平面图. 分析: 平面图判定问题貌似是有线性做法的,这里给出链接,不是本 ...
- P3209 [HNOI2010]平面图判定
P3209 [HNOI2010]平面图判定 哈密尔顿环之外的任意一条边,要么连在环内部,要么连在环外部 判断两条边在同一部分会相交,则这两条边必须分开 那么把边看作点连边,跑二分图染色就行 #incl ...
- Luogu P3209 [HNOI2010]平面图判定(2-SAT)
P3209 [HNOI2010]平面图判定 题意 题目描述 若能将无向图\(G=(V,E)\)画在平面上使得任意两条无重合顶点的边不相交,则称\(G\)是平面图.判定一个图是否为平面图的问题是图论中的 ...
- 洛谷P3209 [HNOI2010]PLANAR(2-SAT)
题目描述 若能将无向图G=(V,E)画在平面上使得任意两条无重合顶点的边不相交,则称G是平面图.判定一个图是否为平面图的问题是图论中的一个重要问题.现在假设你要判定的是一类特殊的图,图中存在一个包含所 ...
- 洛谷P3209 [HNOI2010]PLANAR
首先用一波神奇的操作,平面图边数m<=3*n-6,直接把m降到n, 然后对于冲突的边一条环内,一条环外,可以用并查集或者2Sat做, 当然并查集是无向的,2Sat是有向的,显然用并查集比较好 复 ...
- bzoj1997 [HNOI2010]平面图判定Plana
bzoj1997 [HNOI2010]平面图判定Planar 链接 bzoj luogu 思路 好像有很多种方法过去.我只说2-sat 环上的边,要不在里面,要不在外边. 有的边是不能同时在里面的,可 ...
- [BZOJ1997][HNOI2010] 平面图判定
Description Input Output 是的..BZOJ样例都没给. 题解(from 出题人): 如果只考虑简单的平面图判定,这个问题是非常不好做的. 但是题目中有一个条件— ...
- 洛谷P3203 [HNOI2010]弹飞绵羊(LCT,Splay)
洛谷题目传送门 关于LCT的问题详见我的LCT总结 思路分析 首先分析一下题意.对于每个弹力装置,有且仅有一个位置可以弹到.把这样的一种关系可以视作边. 然后,每个装置一定会往后弹,这不就代表不存在环 ...
- Bzoj2002/洛谷P3203 [HNOI2010]弹飞绵羊(分块)
题面 Bzoj 洛谷 题解 大力分块,分块大小\(\sqrt n\),对于每一个元素记一下跳多少次能跳到下一个块,以及跳到下一个块的哪个位置,修改的时候时候只需要更新元素所在的那一块即可,然后询问也是 ...
随机推荐
- C#语法复习2
第五章 方法 1.方法是一块具有名称的代码 包括:方法体.方法头 局部变量必须被赋值才可以执行下面的操作.实例变量有隐式初始化.有时候,类型推断可以用var关键字,类似于C++当中的auto.用于局部 ...
- 使用string实现一个用于储存那些太大而无法使用 long long 的数
类的定义: class stringInt { public: stringInt(); stringInt(string num); stringInt(int num); stringInt op ...
- Flume-ng-sdk源码分析
Flume 实战(2)--Flume-ng-sdk源码分析 - mumuxinfei - 博客园 http://www.cnblogs.com/mumuxinfei/p/3823266.html
- 百度dureos CMake Error
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage CMake Error: CMAKE_CXX_COMPILER not set, ...
- js appendChild 自带remove和append两个功能
一提到appendChild ,我们就知道是在父节点上增加子节点. 经常使用到它就是动态增加一个元素,将其加入到指定的父节点下. 其实appendChild除了这个功能之外,他还有一个功能:如果子节点 ...
- spring的依赖注入(DI)、控制反转(IOC)和面向切面(AOP)
在spring的配置文件增加 <context:component-scan base-package="com.jmu.ccjoin.service"/> <c ...
- vscode中检测代码中的空白行并去除的方法【转】
按下ctrl+h键进行正则匹配:^\s*(?=\r?$)\n 然后直接替换,再看代码发现空行已经不见了.
- ES6 中的let 声明变量
1.let是声明的是块级变量,不会污染全局,一般条件与循环中会用到: 2.let 不可以变量提升: 3.let不遵循作用域,一个作用域内如果有该变量,就不会到全局去找,也不可以在一个作用域重复声明一 ...
- YTU 1055: 输入字符串以及输出
1055: 输入字符串以及输出 时间限制: 1 Sec 内存限制: 128 MB 提交: 694 解决: 476 题目描述 编写一函数,由实参传来一个字符串,统计此字符串中字母.数字.空格和其它字 ...
- let 命令 与 var的区别
ES6 新增了let命令,用来声明变量.它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效. <script> { let a = 10; var b = 1; } ...