• BZOJ2215[Poi2011]Conspiracy——2-SAT+tarjan缩点


    题目描述

    Byteotia的领土被占领了,国王Byteasar正在打算组织秘密抵抗运动。国王需要选一些人来进行这场运动,而这些人被分为两部分:一部分成为同谋者活动在被占领区域,另一部分是后勤组织在未被占领的领土上运转。但是这里出现了一个问题: 1. 后勤组织里的任意两人都必须是熟人,以促进合作和提高工作效率。 2. 同谋者的团体中任意两人都不能是熟人。 3. 每一部分都至少要有一个人。国王想知道有多少种分配方案满足以上条件,当然也有可能不存在合理方案。现在国王将这个问题交由你来解决!

    输入

    第一行一个整数n(2<=n<=5000)表示有n个人参与该抵抗运动,标号为1..n。 之后有n行,第i行的第一个数ki(0<=ki<=n-1)表示i认识ki个人,随后的ki个数表示i的熟人。 p.s.输入满足:如果i是x的熟人,x会在i的序列中出现同时i也会出现在x的熟人序列中。

    输出

    符合条件的方案总数。

    样例输入

    4
    2 2 3
    2 1 3
    3 1 2 4
    1 3

    样例输出

    3

    提示

    Hint 1和4分到同谋者组织,2和3为后勤组织。 2和4分到同谋者组织,1和3为后勤组织。 4单独分到同谋者组织,1和2、3为后勤组织。

    为了方便假设后勤组织为A,同谋组织为B

    用邻接矩阵存每个人之间的认识情况,如果两个人认识就不能都在B,如果两个人不认识就不能都在A,枚举每个人之间的认识情况来2-SAT连边。

    因为对于任意两个人在两种不同的方案中,他们不能一种方案都在A,另一种方案都在B。

    所以在找到任意一组不冲突方案后,只能通过只将A中一个人移到B中或只将B中一个人移到A中或只将A和B中的各一个人调换来得到其他方案。

    对于每个人,我们预处理出另一个集合中与他冲突的人数,什么是冲突的人?假设x,y认识,x在A中,y在B中,那么y就是x的冲突的人,因为如果x移到B中与y冲突。

    先来考虑交换两个人的方案数。

    如果一个人x的冲突人数>=2显然他不能动。

    如果一个人x的冲突人数=1且与他冲突的人y没有冲突人数或与他冲突的人y只有一个冲突人数且这个冲突的人就是x,那么他们两个可以交换。

    假设A中没有冲突人数的人有a个,B中有b个,那么这些人随便交换,方案数是a*b。

    再来考虑移动一个人的方案数。

    如果一个人没有冲突人数且他当前所在集合不只有他一个人,那么他能移到对面集合。

    最后还要判断最开始随便找的一个不冲突方案是否每个集合都有人。

    #include<set>
    #include<map>
    #include<stack>
    #include<cmath>
    #include<queue>
    #include<vector>
    #include<bitset>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n;
    int k,x;
    int m[5010][5010];
    int vis[10010];
    int dep[10010];
    int low[10010];
    int s[10010];
    vector<int>q[10010];
    int head[10010];
    int to[25000010];
    int next[25000010];
    int top;
    int t[10010];
    int v[10010];
    int num;
    int cnt;
    int tot;
    int now;
    int ans;
    int A[5010];
    int B[5010];
    int a,b;
    int miku[10010];
    int NIE[10010];
    void add(int x,int y)
    {
        tot++;
        next[tot]=head[x];
        head[x]=tot;
        to[tot]=y;
    }
    void tarjan(int x)
    {
        t[++top]=x;
        low[x]=dep[x]=++num;
        vis[x]=1;
        for(int i=head[x];i;i=next[i])
        {
            if(!dep[to[i]])
            {
                tarjan(to[i]);
                low[x]=min(low[x],low[to[i]]);
            }
            else if(vis[to[i]])
            {
                low[x]=min(low[x],dep[to[i]]);
            }
        }
        if(dep[x]==low[x])
        {
            cnt++;
            do
            {
                now=t[top];
                top--;
                s[now]=cnt;
                vis[now]=0;
            }
            while(now!=x);
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&k);
            for(int j=1;j<=k;j++)
            {
                scanf("%d",&x);
                m[i][x]=1;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(m[i][j])
                {
                    add(i+n,j);
                    add(j+n,i);
                }
                else
                {
                    add(i,j+n);
                    add(j,i+n);
                }
            }
        }
        for(int i=1;i<=2*n;i++)
        {
            if(!dep[i])
            {
                tarjan(i);
            }
        }
        for(int i=1;i<=n;i++)
        {
            if(s[i]==s[i+n])
            {
                printf("0");
                return 0;
            }
            if(s[i]<s[i+n])
            {
                A[++a]=i;
            }
            else
            {
                B[++b]=i;
            }
        }
        if(a&&b)
        {
            ans++;
        }
        for(int i=1;i<=a;i++)
        {
            for(int j=1;j<=b;j++)
            {
                if(m[A[i]][B[j]])
                {
                    NIE[A[i]]++;
                    miku[A[i]]=B[j];
                }
            }
        }
        for(int i=1;i<=b;i++)
        {
            for(int j=1;j<=a;j++)
            {
                if(!m[B[i]][A[j]])
                {
                    NIE[B[i]]++;
                    miku[B[i]]=A[j];
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            if(NIE[i]==1)
            {
                if(NIE[miku[i]]==1&&miku[i]>i&&miku[miku[i]]==i)
                {
                    ans++;
                }
                else if(!NIE[miku[i]])
                {
                    ans++;
                }
            }
        }
        int t1=0;
        int t2=0;
        for(int i=1;i<=n;i++)
        {
            if(!NIE[i])
            {
                if(s[i]<s[i+n]&&a>1)
                {
                    ans++;
                }
                if(s[i]>s[i+n]&&b>1)
                {
                    ans++;
                }
                if(s[i]<s[i+n])
                {
                    t1++;
                }
                else
                {
                    t2++;
                }
            }
        }
        ans+=t1*t2;
        printf("%d",ans);
    }
  • 相关阅读:
    SQL中JOIN 的用法
    ava中普通代码块,构造代码块,静态代码块区别及示例
    javabean的内省技术和BeanUtils的使用
    Tomcat服务器学习和使用(一)
    增强For循环
    JAVA单态设计模式
    关于枚举的整理
    java中遍历MAP的几种方法
    equals和==的区别
    深入剖析Java中的装箱和拆箱
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/9719608.html
Copyright © 2020-2023  润新知