zoukankan      html  css  js  c++  java
  • 【BZOJ】3495: PA2010 Riddle 2-SAT算法

    【题意】有n个城镇被分成了k个郡,有m条连接城镇的无向边。要求给每个郡选择一个城镇作为首都,满足每条边至少有一个端点是首都。n,m,k<=10^6。

    【算法】2-SAT,前后缀优化建图

    【题解】每个城镇只有作为首都和不是首都两种选项,即2-sat问题。

    2-sat问题中所有边必须加反向边,下面过程忽略反向边。

    对于一条边(x,y),如果x是0,那么y必须是1,即x-y'。(y-x'是反向边,考虑的时候忽略)

    但是一个郡只有一个首都有点烦,有一种套路叫前后缀优化建图。

    对于每个点x,假设一个点x+n,表示编号<=x的和x同在一个郡的点中是否有首都。

    假设x和y是同一个郡的相邻编号点(y>x),那么:

    1.前缀:(x+n)' - (y+n)'

    2.修改:x' - (x+n)'

    3.只有一个首都:(x+n)' - y

    所以n*4个点和n*8条边,进行2-sat。

    复杂度O(M),M=(m+3*n)*2。

    #include<cctype>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int read(){
        int s=0,t=1;char c;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    const int maxn=4000010;
    int n,m,K,first[maxn],tot,c,B[1000010],C[1000010];
    bool mark[maxn];
    struct edge{int v,from;}e[maxn*2];////
    void insert(int u,int v){
        tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;
        tot++;e[tot].v=u^1;e[tot].from=first[v^1];first[v^1]=tot;
    }
    int dfsnum=0,TOT,top,dfn[maxn],low[maxn],st[maxn],belong[maxn];
    void tarjan(int x){
        dfn[x]=low[x]=++dfsnum;
        st[++top]=x;
        for(int i=first[x];i;i=e[i].from)if(!dfn[e[i].v]){
            tarjan(e[i].v);
            low[x]=min(low[x],low[e[i].v]);
        }else if(!belong[e[i].v])low[x]=min(low[x],dfn[e[i].v]);
        if(low[x]==dfn[x]){
            TOT++;
            while(st[top]!=x)belong[st[top--]]=TOT;
            belong[st[top--]]=TOT;
        }
    }
    int main(){
        n=read();m=read();K=read();
        for(int i=1;i<=m;i++){
            int u=read()-1,v=read()-1;
            insert(u<<1,v<<1|1);
        }
        for(int i=0;i<n;i++)insert(i<<1|1,(i+n)<<1|1);///
        for(int k=1;k<=K;k++){
            int w=read(),p,pre=read()-1;B[pre]=k;
            for(int i=2;i<=w;pre=p,i++){
                p=read()-1;B[p]=k;
                insert((pre+n)<<1|1,(p+n)<<1|1);
                insert((pre+n)<<1|1,p<<1);
            }
        }
        for(int i=0;i<n*4;i+=2){
            if(!dfn[i])tarjan(i);
            if(!dfn[i^1])tarjan(i^1);
            if(belong[i]==belong[i^1]){printf("NIE");return 0;}
        }
        printf("TAK");
        return 0;
    }
    View Code

    从这道题开始,我知道了一件事:理论复杂度O(nm)的DFS版2-sat是可以无视随机被卡TLE的。

    嘤嘤嘤T_T。

  • 相关阅读:
    带编译器的codeblocks下载地址
    联想拯救者s15k重装w10系统教程
    w10下Oracle 11g完全干净卸载
    小机房的树(codevs 2370)
    NOIP[2015] 运输计划(codevs 4632)
    ⑨要写信(codevs 1697)
    酒厂选址(codevs 1507)
    美丽的大树(codevs 2124)
    乘法运算(codevs 3254)
    货车运输(codevs 3287)
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8603058.html
Copyright © 2011-2022 走看看