zoukankan      html  css  js  c++  java
  • 和平委员会

    https://loj.ac/problem/10097

    题目描述

      给出n个党派,每个党派中有2个代表,若两个代表彼此厌恶就都不能成为委员会,求能否成立委员会,若能输出一种可行的方案。

    思路

      典型的2-SAT问题,我们用1表示在委员会中,0表示不在委员会中,那么给出的约束条件就是某两个代表的值必须为有一个0,隐藏的约束条件为同党派两个代表中有且只有一个代表的值为1。因此对于两个彼此厌恶的代表a、b,我们假设同党派另两个代表分别为u、v,那么我们可以将u、b连边,v、a连边,这条边的意思是必须,即这条边所连接的点的值必须相同,而建有向边的原因是就是实际意义,不过事实上我们建的是反图,相当于选a必须选v,选b必须选u。接下来我们只要进行缩点,那么在同一个强连通分量中的点必定是同一个值,而如果同个党派的代表在同一个强连通分量里,显然不可能成立。

      接下来考虑如何构造一组解,在原图上构造一组解,我们显然需要按照原图拓扑排序来,因为无入边的节点显然限制更小。具体的构造方法是对于同一党派的两个人a,b,如果a所在的强连通分量拓扑序在b所在强连通分量的前面,那么就取a的值为1。这样做显然是对的,可以从对称性上去分析,事实上缩点后的图也一定是对称的,那么对于两个党派的四个代表A,A',B,B',如果B'是A的后代节点(A到B’有边),那么A’是B的后代节点,显然我们可以推得A’和B'互相厌恶,由于存的是反图,所以我们可以将不选择的标记进行传递,因此如果确定了A选,那么A'的前代节点肯定都不可选,而图的对称性告诉我们A的后代节点和A’的前代节点没有区别,这样就必定能构造出一组解。

      而实际上在反图上跑tarjan并不影响强连通分量的判定,而反图缩点后的DAG,显然在dfs序上是按照原图缩点后拓扑序排好的,不过是倒过来,也就是说得到的强连通分量的节点编号符合反图上的拓扑序,原图上的逆拓扑序,那就可以直接判定同党派两个代表所在强连通分量的大小即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=8800<<1,M=20005<<1;
    
    int nxt[M],to[M],head[N],tot;
    void add_edge(int x,int y)
    {
        nxt[++tot]=head[x];
        head[x]=tot;
        to[tot]=y;
    }
    
    int read()
    {
        int res=0,w=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+ch-'0';ch=getchar();}
        return res*w;
    }
    
    int dfn[N],low[N],st[N],top,idx,co[N],col;
    void tarjan(int u)
    {
        dfn[u]=low[u]=++idx;
        st[++top]=u;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(!dfn[v])
            {
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(!co[v])low[u]=min(low[u],dfn[v]);
        }
        if(dfn[u]==low[u])
        {
            co[u]=++col;
            while(st[top]!=u)
            {
                co[st[top]]=col;
                --top;
            }
            --top;
        }
    }
    int main() 
    {
        int n,m;
        n=read();m=read();
        for(int i=1;i<=m;i++)
        {
            int a=read(),b=read(),u,v;
            u=(a&1)?a+1:a-1;
            v=(b&1)?b+1:b-1;
            add_edge(u,b);add_edge(v,a);
        }
        for(int i=1;i<=n*2;i++)
            if(!dfn[i])tarjan(i);
        for(int i=1;i<=n;i++)
            if(co[i*2-1]==co[i*2])
            {
                printf("NIE");
                return 0;
            }
        for(int i=1;i<=n;i++)
            printf("%d
    ",co[i*2]>co[i*2-1]?i*2:i*2-1);
        return 0;
    }
  • 相关阅读:
    [hosts]在hosts中屏蔽一级域名和二级域名的写法
    [oracle]查询一个表中数据的插入时间
    [Windows Doc]微软官方文档
    [PL]如果天空是黑暗的,那就摸黑生存
    [LVM]创建LVM卷
    [powershell]获取FCID&Port
    [oracle]解决ora-01034 oracle not available
    [GoogleBlog]new-approach-to-china
    [时钟]配置日期时间并同步到硬件
    [rhel]安装oracle11g
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11734792.html
Copyright © 2011-2022 走看看