【LOJ#536】「LibreOJ Round #6」花札

题目链接

题目描述

「UniversalNO」的规则如下:每张牌有一种颜色和一个点数。两个人轮流出牌,由 Alice 先手,最开始牌堆为空,出的人可以出任意牌(放到牌堆顶),之后出的牌必须和当时牌堆顶的牌的颜色或点数至少有一个相同。有牌可出者必须出,无牌可出者输。

Alice 和 Shinobu 玩了几局后觉得原来的规则太依靠运气,于是她们加了一个新玩法:Alice 出了第一张之后,两个人立即交换手里的牌,然后从 Alice 开始继续按原来的规则进行游戏。当然,这次 Alice 出的牌必须和她刚开始出的颜色或点数至少有一个相同。

交换之后两人都知道对方的手牌(就是开局时自己的手牌),于是就有必胜策略了。

现在已知 Alice 和 Shinobu 手里一开始的牌,请你求出对于 Alice 第一次出牌的每种情况,谁有必胜策略。

Sol

不难看出出了一张牌后另一个人能够出的牌是可以事先知道的。
那么这就形成一个二分图。
博弈过程相当于是一开始在先手的点上放了一个棋子,然后每次轮流沿着出边走,不能走的人失败。

这是一个经典的二分图博弈模型
先手必胜当且仅当棋子所在点一定在最大匹配中。

判断方法:
跑网络流,要求源点到这个点有流量且这个点没有被分割在源点集合。

code:

#include<bits/stdc++.h>
using namespace std;
#define Set(a,b) memset(a,b,sizeof(a))
template<class T>inline void init(T&x){
    x=0;char ch=getchar();bool t=0;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    if(t) x=-x;return;
}typedef long long ll;
const int N=4e4+10;
const int MAXN=2e5+10;
const int MAXM=2e6+10;
int m,c,n1,n2;
struct edge{
    int to,next,cap;
}a[MAXM<<2];
int head[MAXN],cnt=0,cur[MAXN],d[MAXN];
inline void add(int x,int y,int z){a[cnt]=(edge){y,head[x],z};head[x]=cnt++;}
inline void add_edge(int x,int y,int z){add(x,y,z),add(y,x,0);}
int X1[N],X2[N],Y1[N],Y2[N];
int idnum[N],idcol[N],idshi[N];
int S=0,T;
queue<int> Q;
int dfs(int u,int flow){
    if(u==T) return flow;
    int res=flow;
    for(int v,&i=cur[u];~i;i=a[i].next) {
        v=a[i].to;if(!a[i].cap||d[v]!=d[u]+1) continue;
        int f=dfs(v,min(res,a[i].cap));
        a[i].cap-=f,a[i^1].cap+=f;
        res-=f;if(!f) d[v]=0;
        if(!res) break;
    }
    return flow-res;
}
inline bool bfs(){
    for(int i=S;i<=T;++i) d[i]=0;d[S]=1;
    while(!Q.empty()) Q.pop();Q.push(S);
    while(!Q.empty()) {
        int u=Q.front();Q.pop();
        for(int v,i=head[u];~i;i=a[i].next) {
            v=a[i].to;if(!a[i].cap||d[v]) continue;
            d[v]=d[u]+1;if(v==T) return 1;Q.push(v);
        }
    }
    return d[T];
}
inline void Dinic(){
    int fl=0;
    while(bfs()) memcpy(cur,head,sizeof(cur)),fl+=dfs(S,1e9);
    return;
}
int ans[N];
int main()
{
    init(m),init(c);init(n1);
    Set(head,-1);for(int i=1;i<=n1;++i) init(X1[i]),init(Y1[i]);
    init(n2);int h=n1;
    for(int i=1;i<=n2;++i) idshi[i]=++h;
    for(int i=1;i<=m;++i) idnum[i]=++h;
    for(int i=1;i<=c;++i) idcol[i]=++h;
    T=++h;
    for(int i=1;i<=n1;++i) add_edge(S,i,1),add_edge(i,idnum[X1[i]],1),add_edge(i,idcol[Y1[i]],1);
    for(int i=1;i<=n2;++i) init(X2[i]),init(Y2[i]),add_edge(idshi[i],T,1),add_edge(idnum[X2[i]],idshi[i],1),add_edge(idcol[Y2[i]],idshi[i],1);
    Dinic();bfs();
    for(int v,i=head[S];~i;i=a[i].next) {v=a[i].to;if(v>0&&v<=n1&&!a[i].cap&&!d[v]) ans[v]=1;}
    for(int i=1;i<=n1;++i) puts(ans[i]? "1":"0");
    return 0;
}
上一篇:[技术项目3]--流量回放项目总结


下一篇:题解 [SEERC2019]Game on a Tree