[bzoj1997][Hnoi2010]Planar(2-sat||括号序列)

  开始填连通分量的大坑了= =

  然后平面图有个性质m<=3*n-6.....

  由平面图的欧拉定理n-m+r=2(r为平面图的面的个数),在极大平面图的情况可以代入得到m=3*n-6。

  网上的证明(雾?)

 http://blog.chinaunix.net/uid-26510579-id-3183558.html

 http://www.zybang.com/question/673815bbe56e8b5639f95234b515b8c5.html

  这题把哈密顿回路看成圆,就变成圆上的点之间的边是否能不相交。。和某次模拟赛的T3一模一样= =

  显然对于两条会相交的边x,y,x和y既不能同时在圆内,也不能同时在圆外。。。就转换成2-sat问题了。。

  若使x表示x在圆内,x'表示x在圆外,因为x,y不能同时在圆内,所以连上(x,y')和(y,x'),然后还不能同时在圆外,就再连(x',y)和(y',x)。

  然后就是模板了。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=204;
const int maxm=723333;
struct zs{
    int too,pre;
}e[maxm];
struct zzs{
    int from,too;
}a[maxn*3];
int last[maxn*3*2],dfn[maxn*3*2],low[maxn*3*2],st[maxn*3*2],bel[maxn*3*2];
bool ins[maxn*3*2];
int from[maxn*3],too[maxn*3],id[maxn];
int tot,tim,num,i,j,k,n,m,x,y,top,tt;
inline bool cant(int x,int b){
    if(a[x].from>a[b].from)swap(x,b);
    if(a[b].from>a[x].from&&a[b].from<a[x].too&&a[b].too>a[x].too)return 1;
    return 0;
}
void insert(int a,int b){
//    printf("%d-->%d\n",a,b);
    e[++tot].too=b;e[tot].pre=last[a];last[a]=tot;
    e[++tot].too=a;e[tot].pre=last[b];last[b]=tot;
}
void tarjan(int x){
    dfn[x]=low[x]=++tim;
    st[++top]=x;;ins[x]=1;
    for(int i=last[x];i;i=e[i].pre)if(!dfn[e[i].too]){
        tarjan(e[i].too);low[x]=min(low[x],low[e[i].too]);
    }else if(ins[e[i].too])low[x]=min(low[x],dfn[e[i].too]);
    if(dfn[x]==low[x]){
        num++;
        while(st[top+1]!=x){
            ins[st[top]]=0;bel[st[top]]=num;
            top--;
        }
    }
}
bool cmp(zzs a,zzs b){
    return a.from<b.from||(a.from==b.from&&a.too<b.too);
}
int main(){
    scanf("%d",&tt);
    while(tt--){
        scanf("%d%d",&n,&m);
        if(m>n*3-6){
            for(i=1;i<=m<<1;i++)scanf("%d",&j);
            for(i=1;i<=n;i++)scanf("%d",&j);
            printf("NO\n");continue;
        }
        top=tim=tot=num=0;
        memset(dfn,0,4*(2*m+1));
        memset(last,0,4*(2*m+1));
        for(i=1;i<=m;i++)scanf("%d%d",&a[i].from,&a[i].too);
            
        for(i=1;i<=n;i++)scanf("%d",&j),id[j]=i;
        for(i=1;i<=m;i++){
            a[i].from=id[a[i].from];a[i].too=id[a[i].too];
            if(a[i].from>a[i].too)swap(a[i].from,a[i].too);
        }//按哈密顿回路给点重新编号,使1~n依次对应环中的点 
        sort(a+1,a+1+m,cmp);j=0;
        for(i=1;i<=m;i++){
            if(a[i].from+1==a[i].too||a[i].too%n+1==a[i].from)continue;
            j++;
            a[j].from=a[i].from,a[j].too=a[i].too;
        }
        m=j;
        for(i=1;i<m;i++)for(j=i+1;j<=m;j++){
            if(cant(i,j))insert(i*2,j*2-1),insert(j*2,i*2-1);//,printf("%d&&&%d\n",i,j);
            if(a[j].from>=a[i].too)break;
        }
        for(i=1;i<=2*m;i++)if(!dfn[i])tarjan(i);
        bool flag=0;
        for(i=1;i<=m;i++)if(bel[i*2]==bel[i*2-1]){flag=1;break;
        }
        if(flag)printf("NO\n");else printf("YES\n");
    }
    return 0;
}
View Code

  系统:正在比对你的代码和黄学长的代码。。。。

     找不到差异QAQ

  当然了kpm大爷那场是玩成括号序列。。。。把边的两端点看成是左括号和右括号,那么圆内和圆外分别是一个合法的括号序列。。算括号序列的时候记录一下每条边会与别的哪些边冲突,按冲突关系建图后二分染色就知道是否可能合法了。。。

  感觉也可以两个括号序列一起上。。其中一个出现冲突后就把冲突的那些边都扔到另外一个括号序列里面,如果再冲突就是无解了。。当然了我只是嘴巴选手(跑

 

1997: [Hnoi2010]Planar

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1093  Solved: 428
[Submit][Status][Discuss]

Description

Input

Output

Sample Input

 

Sample Output

 

HINT

 

Source

Day1

//没有样例差评

posted @ 2015-10-03 00:21  czllgzmzl  阅读(246)  评论(0编辑  收藏  举报