GYM 101128 B.Black Vienna(并查集)

本文介绍了一种基于牌类游戏的算法问题,探讨了如何通过并查集等数据结构来解决游戏中的特定查询问题,包括如何处理玩家持有牌张数量的查询及验证不同场景下方案的可行性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
有A~Z这26张牌,现在从中抽出三张并把剩余23张分给选手1和2,现在有n次查询,每次询问一个选手是否有某两张牌,和选手的回答,回答是说自己有这两张牌中的几张,问拿出的三张牌有多少种方案能够满足这n个条件
Input
第一行一整数n表示查询数,每组查询输入两个字符表示要查询的两张牌,以及要查询的选手编号(1or2),以及选手的回答(0,1,2)表示这两张牌手里有多少张(0<=n<=50)
Output
输出合法方案数
Sample Input
3
AB 1 1
AC 2 1
BC 2 1
Sample Output
506
Solution
首先处理0张和2张的情况,遇见不合法情况直接gg,那么剩下的就是1张的情况,枚举拿出的三张牌用并查集判是否合法,i表示1选手拿第i张牌,i+26表示2选手拿第i张牌,0表示一定拿,53表示一定拿,对于拿出的三张牌x,y,z,如果某位选手拿了则不合法,没有则两位选手都不拿这三张牌,之后处理1张查询中涉及到x,y,z这三张牌的查询,设这次查询问的是a,b,如果a,b都属于这三张牌则gg;如果没有属于这三种牌的则把1拿a和2拿b合并,2拿a和1拿b合并;如果a,b有一张属于这三张牌,那么该选手一定拿另一张牌,不妨另一张牌是b,那么就是当前选手一定拿b另一选手一定不拿b,这样就处理完1张的查询,然后处理除x,y,z之外某些被确定拿或不拿的牌,如果i牌两个人都一定不拿或一定拿则gg,如果一张牌1一定拿2一定不拿,则把i和0合并,i+26和53合并,如果1一定不拿2一定拿,则把i和53合并,i+26和0合并,所有合并操作结束,判断fa[i]和fa[i+26]是否相同,如果相同说明两位选手拿了同一张牌,不合法;然后判断fa[0]和fa[53]是否相同,如果相同说明一位选手拿某张牌又不拿某张牌才会把0和53合并,不合法;否则合法
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<string>
using namespace std;
#define maxn 55
int fa[maxn];
void init(int n)
{
    for(int i=0;i<=n;i++)fa[i]=i;
}
int find(int x)
{
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
    x=find(x),y=find(y);
    if(x==y)return ;
    fa[x]=y; 
}
int n,q[55][3],res,vis[2][33],mark[2][33],used[55];
char op[5];
bool check(int x,int y,int z)
{
    init(53);
    for(int i=0;i<2;i++)
        for(int j=1;j<=26;j++)
            mark[i][j]=vis[i][j];
    for(int i=0;i<2;i++)
    {
        if(mark[i][x]==1||mark[i][y]==1||mark[i][z]==1)return 0;
        mark[i][x]=mark[i][y]=mark[i][z]=-1;
    }
    for(int i=1;i<=res;i++)
    {
        int f1=0,f2=0,a=q[i][0],b=q[i][1],c=q[i][2];
        if(b==x||b==y||b==z)f1=1;
        if(c==x||c==y||c==z)f2=1;
        if(f1&&f2)return 0;
        if(f2)swap(b,c),swap(f1,f2);
        if(f1)
        {
            int t1=a*26+c,t2=(a^1)*26+c;
            unite(t1,0),unite(t2,53);
        }
        else
        {
            int t1=a*26+b,t2=a*26+c,t3=(a^1)*26+b,t4=(a^1)*26+c;
            unite(t1,t4),unite(t2,t3);
        }
    }
    for(int i=1;i<=26;i++)
        if(i!=x&&i!=y&&i!=z)
        {
            if(mark[0][i]==1&&mark[1][i]==1)return 0;
            if(mark[0][i]==-1&&mark[1][i]==-1)return 0;
            if(mark[0][i]==1||mark[1][i]==-1)unite(i,0),unite(26+i,53);
            else if(mark[1][i]==1||mark[0][i]==-1)unite(i,53),unite(26+i,0);
        }
    if(fa[0]==fa[53])return 0;
    for(int i=1;i<=26;i++)
        if(fa[i]==fa[i+26])return 0;
    return 1;
}
int main()
{
    while(~scanf("%d",&n))
    {
        res=0;
        memset(vis,0,sizeof(vis));
        int gg=0;
        for(int i=1;i<=n;i++)
        {
            int a,b;
            scanf("%s%d%d",op,&a,&b);
            a--;
            int c=op[0]-'A'+1,d=op[1]-'A'+1;
            if(c>d)swap(c,d);
            if(b==0)
            {
                if(vis[a][c]==1||vis[a][d]==1)gg=1;
                else vis[a][c]=vis[a][d]=-1;
            }
            else if(b==2)
            {
                if(vis[a][c]==-1||vis[a][d]==-1)gg=1;
                else vis[a][c]=vis[a][d]=1;
            }
            else q[++res][0]=a,q[res][1]=c,q[res][2]=d;
        }   
        int ans=0;
        if(!gg)
        {
            for(int i=1;i<=26;i++)
                for(int j=i+1;j<=26;j++)
                    for(int k=j+1;k<=26;k++)
                        if(check(i,j,k))ans++;
        }
        printf("%d\n",ans);

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值