zoukankan      html  css  js  c++  java
  • bzoj2215: [Poi2011]Conspiracy

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2215

    思路:一道很好的2-sat题

    首先一个人要么分配给同谋者,要么分配给后勤组织

    这可以考虑2-sat

    那么怎么连边?这个很显然

    如果(i,j)有边,那么一个在同谋者,则另一个必不在同谋者

    如果(i,j)无边,那么一个在后勤组织,另一个必不在后勤组织


    然后考虑求方案数

    先求出一种方案

    然后我们就会发现一个性质

    只会有一个人被换过去,或者换过来

    因为两个后勤组织的人一定有边,不可能同时换过去

    同理因为两个同谋者的人一定无边,不可能同时换过去


    于是就可以愉(keng)快(die)地分类讨论了


    首先有三种情况

    1:从后勤组织过去一个人

    2:从同谋者过去一个人

    3:两个组织互换一个人


    先预处理出每个人到另一个组织后会有几个人和他冲突,如果只有一个,和他冲突的是谁

    对于过去一个人的情况

    只要无人与他冲突并且过去后还有人即可方案数+1


    对于交换的情况

    1.如果i有一个冲突的人j,那么如果j也只有一个冲突的人并且j冲突的人是i,那么方案数+1,但为了不重复,只在i<j时统计

    2.如果i有一个冲突的人j,j没有冲突的人,那么方案数+1,这种不会重复

    3.如果i没有冲突的人,那么它和另一个团体中任意一个也没有冲突的人可以互换

    记录后勤组织交换后无冲突的人数t0,同谋者交换后无冲突的人数t1

    方案数+=t1*t2


    然后就没有然后了



    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    const int maxn=10010,maxm=50000010;
    using namespace std;
    int n,low[maxn],dfn[maxn],tim,sta[maxn],top,bel[maxn],bcnt,cnt1,cnt0,boom[maxn],ans[maxn];bool g[5010][5010],ins[maxn];
    int pre[maxm],now[maxn],son[maxm],tot,cnt[maxn],res;char ch;
    int p0(int x){return x<<1;}
    int p1(int x){return (x<<1)|1;}
    void read(int &x){
    	for (ch=getchar();!isdigit(ch);ch=getchar());
    	for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    }
    //0是后勤(有边) 1是同谋者(无边)
    void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
    void tarjan(int x){
    	dfn[x]=low[x]=++tim,ins[x]=1,sta[++top]=x;
    	for (int y=now[x];y;y=pre[y]){
    		if (!dfn[son[y]]) tarjan(son[y]),low[x]=min(low[x],low[son[y]]);
    		else if (ins[son[y]]) low[x]=min(low[x],dfn[son[y]]);
    	}
    	if (dfn[x]==low[x]){
    		int xx;bcnt++;
    		do{xx=sta[top--],ins[xx]=0,bel[xx]=bcnt;}while (x!=xx); 
    	}
    }
    
    void init(){
    	scanf("%d",&n);
    	for (int i=1,k;i<=n;i++){
    		read(k);
    		for (int j=1,x;j<=k;j++) read(x),g[i][x]=g[x][i]=1;
    	}
    	//for (int i=1;i<=n;i++,puts("")) for (int j=1;j<=n;j++) printf("%d ",g[i][j]);
    	for (int a=1;a<=n;a++) for (int b=1;b<a;b++){
    		if (g[a][b]) add(p1(a),p0(b)),add(p1(b),p0(a));
    		else add(p0(a),p1(b)),add(p0(b),p1(a));
    	}
    }
    
    void work(){
    	for (int i=2;i<=p1(n);i++) if (!dfn[i]) tarjan(i);
    	for (int i=1;i<=n;i++) if (bel[p0(i)]==bel[p1(i)]) return;
    	for (int i=1;i<=n;i++){
    		ans[i]=(bel[p0(i)]>bel[p1(i)]);
    		if (ans[i]) cnt1++;else cnt0++;
    	}
    	//for (int i=1;i<=n;i++) printf("%d %d
    ",i,ans[i]);
    	//if (!cnt0||!cnt1) return;
    	res=(cnt0&&cnt1);int t0=0,t1=0;
    	for (int i=1;i<=n;i++){
    		if (ans[i]){
    			for (int j=1;j<=n;j++) if (i!=j&&!ans[j]&&!g[i][j])
    				cnt[i]++,boom[i]=j;
    		}
    		else{
    			for (int j=1;j<=n;j++) if (i!=j&&ans[j]&&g[i][j])
    				cnt[i]++,boom[i]=j;
    		}
    	}
    	//for (int i=1;i<=n;i++) printf("%d %d %d %d
    ",i,ans[i],cnt[i],boom[i]);
    	for (int i=1;i<=n;i++) if (!ans[i]){
    		if (!cnt[i]&&cnt0>1) res++;
    		if (!cnt[i]) t0++;
    		if (cnt[i]==1)
    			if ((boom[boom[i]]==i&&cnt[boom[i]]==1&&i<boom[i])||!cnt[boom[i]]) res++;
    	}
    	for (int i=1;i<=n;i++) if (ans[i]){
    		if (!cnt[i]&&cnt1>1) res++;
    		if (!cnt[i]) t1++;
    		if (cnt[i]==1)
    			if ((boom[boom[i]]==i&&cnt[boom[i]]==1&&i<boom[i])||!cnt[boom[i]]) res++;
    	}
    	res+=t0*t1;
    }
    
    int main(){
    	init(),work(),printf("%d
    ",res);
    	return 0;
    }


  • 相关阅读:
    谈谈-开源数据库LitePal
    谈谈-Android-PickerView系列之封装(三)
    谈谈-Android-PickerView系列之源码解析(二)
    谈谈-Android-PickerView系列之介绍与使用(一)
    谈谈-ConstraintLayout完全解析
    问题解决-Failed to resolve: com.android.support.constraint:constraint-layout:1.0.0-alpha7
    谈谈-BaseAdapter的封装
    谈谈-使用SparseArray和ArrayMap代替HashMap
    Spring 集成Redis Cluster模式
    Redis三种集群模式-Cluster集群模式
  • 原文地址:https://www.cnblogs.com/thythy/p/5493473.html
Copyright © 2011-2022 走看看