2020CCPC长春H.Combination Lock(二分图博弈)

  给一把锁,有一些数字被试过了,最多五个数字,每次将一个数字转动1(0-9成环),然后尝试能不能打开,每个数字只能尝试一次,问先手还是后手必胜。

  比赛的时候看了这个题,当时觉得不是计算sg函数的博弈,像是图论,模型转换也都比较对,但是根本没见过二分图博弈,实在想不出来,结束看是个很裸的二分图博弈。

  可以看出每次仅转动一个数字,必然导致所有数字的和奇偶发生变化,所以所有的数字构成一个二分图,剩下就是一个裸的二分图博弈了,看了https://www.pianshen.com/article/14591177358/这篇博客,觉得写得很清楚了。之前完全没做过二分图博弈的题,据cjb说去年ICPC南昌网络赛有过二分图博弈,南昌是去年唯一一场写过总结也是打的最炸的一场网络赛,然而我一点印象没有,只记得那场卡一个数学和dijkstra半天???只需要跑一个二分图匹配就好了,HK或者dinic都能过,复杂度$nsqrt(n)$

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <ll,ll> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define all(x) x.begin(),x.end()
#define pb push_back
#define fi first
#define se second
#define mes(a,b) memset(a,b,sizeof a)
#define mp make_pair
#define dd(x) cout<<#x<<"="<<x<<" "
#define de(x) cout<<#x<<"="<<x<<"\n"
#define debug() cout<<"I love Miyamizu Mitsuha forever.\n"
const int inf=0x3f3f3f3f;
const int N=400010,M=5000010;
const int maxn=1e5+5;

bool ban[maxn];
int way[maxn];
int head[N],ver[M],edge[M],Next[M],d[N];
int e[maxn];
int n,s,t,tot,maxflow;
queue<int> q;
vector<int> v[maxn];

void add(int x,int y,int z)
{
//    dd(x);dd(y);de(z);
    ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
    ver[++tot]=x;edge[tot]=0,Next[tot]=head[y],head[y]=tot;
}
bool bfs()
{
    mes(d,0);
    while(!q.empty()) q.pop();
    q.push(s);
    d[s]=1;
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=Next[i])
            if(edge[i]&&!d[ver[i]])
            {
                q.push(ver[i]);
                d[ver[i]]=d[x]+1;
                if(ver[i]==t) return 1;
            }
    }
    return 0;
}
int dinic(int x,ll flow)
{
    if(x==t) return flow;
    int rest=flow,k;
    for(int i=head[x];i&&rest;i=Next[i])
    {
        if(edge[i]&&d[ver[i]]==d[x]+1)
        {
            k=dinic(ver[i],min(rest,edge[i]));
            if(!k) d[ver[i]]=0;
            edge[i]-=k;
            edge[i^1]+=k;
            rest-=k;
        }
    }
    return flow-rest;
}

int run(int l,int nn)
{
    tot=1;
    s=nn,t=nn+1;
    rept(i,0,t) head[i]=0;
    rep(i,0,nn)
    {
        if(ban[i]) continue;
        if(way[i])
        {
            e[i]=tot+1;
            add(i,t,1);
            continue;
        }
        e[i]=tot+1;
        add(s,i,1);
        rep(j,0,2*l)
        {
            if(ban[v[i][j]]||v[i][j]>=nn) continue;
            add(i,v[i][j],1);
        }
    }
    int flow=0;
    maxflow=0;
    while(bfs())
        while(flow=dinic(s,inf)) maxflow+=flow;
    return maxflow;
}

void test()
{
    int l,m,st;
    cin>>l>>m>>st;
    int nn=1;
    rep(i,0,l) nn*=10;
    rep(i,0,nn) ban[i]=0;
    rep(i,0,m)
    {
        int x;
        cin>>x;
        ban[x]=1;
    }
    int ans1=run(l,nn);
    if(edge[e[st]])
    {
        cout<<"Bob\n";
        return ;
    }
    ban[st]=1;
    int ans2=run(l,nn);
    if(ans1==ans2) cout<<"Bob\n";
    else cout<<"Alice\n";
}

int cal(int x)
{
    int ans=0;
    while(x)
    {
        ans+=x%10;
        x/=10;
    }
    return ans%2;
}

void init()
{
    int n=100000;
    rep(i,0,n)
    {
        way[i]=cal(i);
        int x=i,mul=1;
        rep(j,0,5)
        {
            if(x%10==0) v[i].pb(i+9*mul);
            else v[i].pb(i-mul);
            if(x%10==9) v[i].pb(i-9*mul);
            else v[i].pb(i+mul);
            mul*=10;
            x/=10;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    init();
    int q;
    cin>>q;
    while(q--) test();
    return 0;
}

 

posted @ 2020-11-09 00:54  GGMU  阅读(330)  评论(0编辑  收藏  举报