• 洛谷P3513 [POI2011]KON-Conspiracy


    洛谷P3513 [POI2011]KON-Conspiracy

    题目描述

    Byteotia的领土被占领了,国王Byteasar正在打算组织秘密抵抗运动。

    国王需要选一些人来进行这场运动,而这些人被分为两部分:一部分成为同谋者活动在被占领区域,另一部分是后勤组织在未被占领的领土上运转。

    但是这里出现了一个问题:

    1、后勤组织里的任意两人都必须是熟人,以促进合作和提高工作效率。
    2、同谋者的团体中任意两人都不能是熟人。
    3、每一部分都至少要有一个人。国王想知道有多少种分配方案满足以上条件,当然也有可能不存在合理方案。

    现在国王将这个问题交由你来解决!

    分析

    如果没有输出方案数,那么这一道题就是一个裸的(2-SAT)问题

    我们将一个点拆成两个点

    其中编号为(1-n)的代表后勤,编号为(n+1-2n)的代表同谋

    如果(i)(j)是熟人,那么我们从(i+n)(j)建一条边

    如果(i)(j)不是熟人,那么我们从(i)(j+n)建一条边

    我们按照正常的流程跑一个(Tarjan)就可以了

    方案数为(0)的情况比较好求,即出现(shuyu[i]=shuyu[i+n])的情况

    对于有解的情况,我们要分类讨论

    首先我们将所有的点分成两个集合,一个集合为后勤,另一个集合为同谋

    对于后勤中的某个点,如果他和同谋中的某个点是熟人,那么我们就不能将该点加入同谋的集合

    同样地,对于同谋中的某个点,如果他和后勤中的某个点不是熟人,那么我们就不能将该点加入后勤的集合

    我们将这样的点称为冲突点

    我们对于每一个点,都找出它的所有冲突点

    一个显然的结论是,我们不能从一个集合移动两个点到达另一个集合

    这样必定会产生冲突

    因为如果我们将后勤集合中的两个点扔到同谋集合,那么同谋集合会出现熟人,反之亦然

    因此,我们每次最多只能改变一个点的位置

    因此,对于冲突点的数量大于(2)的节点,我们不去考虑它

    如果某一个节点的冲突点的数量为(1),那么我们可以把该节点的冲突点拿到当前节所在的集合

    前提是该节点的冲突点的冲突点的数量为(0)

    如果节点的冲突点的数量为(0),那么我们可以将其扔到另一个集合中

    同时,如果处在不同集合的两个点的冲突数量都为(0),我们可以将这两个点交换,我们用乘法原理解决即可

    代码

    #define fastcall __attribute__((optimize("-O3")))
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize("inline")
    #pragma GCC optimize("-fgcse")
    #pragma GCC optimize("-fgcse-lm")
    #pragma GCC optimize("-fipa-sra")
    #pragma GCC optimize("-ftree-pre")
    #pragma GCC optimize("-ftree-vrp")
    #pragma GCC optimize("-fpeephole2")
    #pragma GCC optimize("-ffast-math")
    #pragma GCC optimize("-fsched-spec")
    #pragma GCC optimize("unroll-loops")
    #pragma GCC optimize("-falign-jumps")
    #pragma GCC optimize("-falign-loops")
    #pragma GCC optimize("-falign-labels")
    #pragma GCC optimize("-fdevirtualize")
    #pragma GCC optimize("-fcaller-saves")
    #pragma GCC optimize("-fcrossjumping")
    #pragma GCC optimize("-fthread-jumps")
    #pragma GCC optimize("-funroll-loops")
    #pragma GCC optimize("-freorder-blocks")
    #pragma GCC optimize("-fschedule-insns")
    #pragma GCC optimize("inline-functions")
    #pragma GCC optimize("-ftree-tail-merge")
    #pragma GCC optimize("-fschedule-insns2")
    #pragma GCC optimize("-fstrict-aliasing")
    #pragma GCC optimize("-falign-functions")
    #pragma GCC optimize("-fcse-follow-jumps")
    #pragma GCC optimize("-fsched-interblock")
    #pragma GCC optimize("-fpartial-inlining")
    #pragma GCC optimize("no-stack-protector")
    #pragma GCC optimize("-freorder-functions")
    #pragma GCC optimize("-findirect-inlining")
    #pragma GCC optimize("-fhoist-adjacent-loads")
    #pragma GCC optimize("-frerun-cse-after-loop")
    #pragma GCC optimize("inline-small-functions")
    #pragma GCC optimize("-finline-small-functions")
    #pragma GCC optimize("-ftree-switch-conversion")
    #pragma GCC optimize("-foptimize-sibling-calls")
    #pragma GCC optimize("-fexpensive-optimizations")
    #pragma GCC optimize("inline-functions-called-once")
    #pragma GCC optimize("-fdelete-null-pointer-checks")
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn=5e3+5;
    inline int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') f=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*f;
    }
    struct asd{
    	int to,next;
    }b[maxn*maxn];
    int head[maxn],tot=1;
    void ad(int aa,int bb){
    	b[tot].to=bb;
    	b[tot].next=head[aa];
    	head[aa]=tot++;
    }
    int dfn[maxn],low[maxn],dfnc,sta[maxn],top,shuyu[maxn],js;
    bool vis[maxn][maxn];
    void tar(int xx){
    	dfn[xx]=low[xx]=++dfnc;
    	sta[++top]=xx;
    	for(int i=head[xx];i!=-1;i=b[i].next){
    		int u=b[i].to;
    		if(!dfn[u]){
    			tar(u);
    			low[xx]=min(low[xx],low[u]);
    		} else if(!shuyu[u]){
    			low[xx]=min(low[xx],dfn[u]);
    		}
    	}
    	if(low[xx]==dfn[xx]){
    		js++;
    		while(1){
    			int y=sta[top--];
    			shuyu[y]=js;
    			if(y==xx) break;
    		}
    	}
    }
    int hq[maxn],tm[maxn],jlhq,jltm,ctd[maxn],mat[maxn];
    bool istm[maxn];
    int main(){
    	memset(head,-1,sizeof(head));
    	int n;
    	n=read();
    	for(int i=1;i<=n;i++){
    		int t;
    		t=read();
    		for(int j=1;j<=t;j++){
    			int aa;
    			aa=read();
    			vis[i][aa]=1;
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=n;j++){
    			if(i==j) continue;
    			if(vis[i][j]) ad(i+n,j);
    			else ad(i,j+n);
    		}
    	}
    	for(int i=1;i<=n*2;i++){
    		if(!dfn[i]) tar(i);
    	}
    	for(int i=1;i<=n;i++){
    		if(shuyu[i]==shuyu[i+n]){
    			printf("0
    ");
    			exit(0);
    		} else if(shuyu[i]<shuyu[n+i]){
    			hq[++jlhq]=i;
    		} else {
    			tm[++jltm]=i;
    			istm[i]=1;
    		}
    	}
    	int ans=(jlhq&&jltm),tmp1=0,tmp2=0;
    	for(int i=1;i<=jlhq;i++){
    		for(int j=1;j<=jltm;j++){
    			if(vis[hq[i]][tm[j]]){
    				++ctd[hq[i]];
    				mat[hq[i]]=tm[j];
    			}
    		}
    	}
    	for(int i=1;i<=jltm;i++){
    		for(int j=1;j<=jlhq;j++){
    			if(!vis[tm[i]][hq[j]]){
    				++ctd[tm[i]];
    				mat[tm[i]]=hq[j];
    			}
    		}
    	}
    	for(int i=1;i<=n;i++){
    		if(ctd[i]==1){
    			if(ctd[mat[i]]==0) ans++;
    		}
    	}
    	for(int i=1;i<=n;i++){
    		if(ctd[i]==0){
    			if((istm[i] && jltm>1) || (!istm[i] && jlhq>1)) ans++;
    			if(istm[i]) tmp1++;
    			else tmp2++;
    		}
    	}
    	printf("%d
    ",ans+tmp1*tmp2);
    	return 0;
    }
    
    
    
  • 相关阅读:
    如何获取喜欢的微信公众号封面图
    array_filter — 用回调函数过滤数组中的单元
    H5实现长按复制
    微信第三方平台 授权链接 报错
    Oracle 查询函数、存储过程、触发器、表、视图等
    Linux查看端口、及根据进程PID查看对应应用端口
    【Sed】sed n与正则
    【Android】android编译之source build/envsetup.sh简单用法
    一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)
    数据库系列:高并发下的数据字段变更
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/13456882.html
Copyright © 2020-2023  润新知