Gym 101128 B Black Vienna
Posted 蒟蒻LQL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gym 101128 B Black Vienna相关的知识,希望对你有一定的参考价值。
题意
有A-Z 26张牌,现在从中抽出3张牌,并把剩下的23张牌分给选手1和2,现在有n次询问,每次询问一个选手是否有某两张牌,和选手的回答。回答说自己有这两张牌中的几张,问拿出的三张牌有多少种方案能够满足这n个条件?n<=50
分析
个人感觉这个题是个很不错的题呢。
并查集的应用,有点像那道“关押罪犯”的升级版?www.cnblogs.com/LQLlulu/p/8819599.html
数据很小,只有26张牌和50个询问。那么三重循环枚举抽哪三张牌。对于每次抽出的三张牌,判断一下能否满足这n个询问,如果满足的话ans++。
但是怎么判断?
因为只有两个玩家,那么对于每张牌,如果这张牌没有被抽走,那么要么属于玩家1,要么属于玩家2。对于大多数情况我们都可以通过记录每张牌的归属判断是否冲突。
但是有一种特殊情况:
此时两张牌都未被抽走,而且回答是1。也就是说,这两张牌里有一张是属于这个人,另一张属于另一个玩家。那么我们此时没法直接记录。因为我们并不能知道哪张牌属于谁,只能确定这两张牌不属于同一个人!没错!这句话表明了要用并查集!
对于每个上述的情况:x=find(a),y=find(b),如果x和y相等说明两者在同一个人手中,那么直接返回false,否则的话我们把他们和另一个的补集相连,p[x]=find(b+n),p[y]=find(a+n)。这代表两者不在同一个集合(因为只有两个集合,所以满足敌人的敌人就是朋友)。
然后对于每个并查集就分给一个人,判断是否会冲突,如果冲突返回false。
如果上述都没有冲突,则返回true。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <iostream> 5 6 using namespace std; 7 const int maxn=100+10; 8 int n; 9 int p[maxn]; 10 struct Node{ 11 char s[2]; 12 int who; 13 int num; 14 }node[maxn]; 15 int ans; 16 int Wh[maxn],val[maxn]; 17 int find(int x){ 18 return p[x]==x?x:p[x]=find(p[x]); 19 } 20 bool check(int A,int B,int C){ 21 for(int i=1;i<=60;i++)p[i]=i; 22 memset(Wh,-1,sizeof(Wh)); 23 bool ok=1; 24 for(int i=1;i<=n;i++){ 25 int a=node[i].s[0]-\'A\'+1; 26 int b=node[i].s[1]-\'A\'+1; 27 int w=node[i].who; 28 // cout<<a<<" "<<b<<" "<<node[i].num<<endl; 29 30 if(node[i].num==2){ 31 if(a==A||a==B||a==C||b==A||b==B||b==C){ 32 //cout<<"-1"<<endl; 33 ok=0; 34 break; 35 } 36 if((Wh[a]==(w^1))||(Wh[b]==(w^1))){ 37 ok=0; 38 break; 39 } 40 // cout<<-1<<endl; 41 Wh[a]=w,Wh[b]=w; 42 } 43 if(node[i].num==0){ 44 if((a==A||a==B||a==C)&&(b==A||b==B||b==C)) 45 continue; 46 else if(a==A||a==B||a==C){ 47 if(Wh[b]==w){ 48 ok=0; 49 break; 50 } 51 Wh[b]=(w^1); 52 } 53 else if(b==A||b==B||b==C){ 54 if(Wh[a]==w){ 55 ok=0; 56 break; 57 } 58 Wh[a]=(w^1); 59 } 60 else{ 61 if(Wh[a]==w||Wh[b]==w){ 62 ok=0; 63 break; 64 } 65 Wh[a]=(w^1),Wh[b]=(w^1); 66 } 67 } 68 if(node[i].num==1){ 69 if((a==A||a==B||a==C)&&(b==A||b==B||b==C)){ 70 ok=0; 71 break; 72 } 73 else if(a==A||a==B||a==C){ 74 if(Wh[b]==(w^1)){ 75 ok=0; 76 break; 77 } 78 Wh[b]=w; 79 } 80 else if(b==A||b==B||b==C){ 81 if(Wh[a]==(w^1)){ 82 ok=0; 83 break; 84 } 85 Wh[a]=w; 86 }else{ 87 int x=find(a),y=find(b); 88 if(x==y){ 89 ok=0; 90 break; 91 } 92 p[x]=find(b+26); 93 p[y]=find(a+26); 94 } 95 } 96 } 97 if(!ok)return false; 98 99 memset(val,-1,sizeof(val)); 100 for(int i=1;i<=26;i++){ 101 if(Wh[i]!=-1){ 102 int f=find(i); 103 if(val[f]== 104 (Wh[i]^1)){ 105 ok=0; 106 // cout<<-1<<endl; 107 break; 108 } 109 val[f]=Wh[i]; 110 f=find(i+26); 111 // cout<<Wh[i]<<endl; 112 if(val[f]==Wh[i]){ 113 // cout<<val[f]<<" "<<Wh[i]<<endl; 114 ok=0; 115 break; 116 } 117 val[f]=(Wh[i]^1); 118 } 119 } 120 if(!ok)return false; 121 122 for(int i=1;i<=26;i++){ 123 int x=find(i),y=find(i+26); 124 if(x==y||(val[x]==val[y]&&val[x]!=-1)){ 125 ok=0; 126 break; 127 } 128 } 129 if(!ok)return false; 130 return true; 131 } 132 int main(){ 133 ans=0; 134 scanf("%d",&n); 135 for(int i=1;i<=n;i++){ 136 scanf("%s%d%d",node[i].s,&node[i].who,&node[i].num); 137 node[i].who--; 138 } 139 140 for(int i=1;i<=26;i++){ 141 for(int j=i+1;j<=26;j++){ 142 for(int k=j+1;k<=26;k++){ 143 if(check(i,j,k)) 144 ans++; 145 } 146 } 147 } 148 cout<<ans; 149 return 0; 150 }
以上是关于Gym 101128 B Black Vienna的主要内容,如果未能解决你的问题,请参考以下文章
GYM 101128 J.Saint John Festival(求凸包是否包含点)
很好的脑洞题:dfs+暴力 Gym - 101128A Promotions
Gym 101128F Sheldon Numbers(网络流)