# Bipartite graph game learning notes

reference resources: https://zhuanlan.zhihu.com/p/359334008

Bipartite graph game is A kind of game model. It can be abstracted as: give A bipartite graph and the starting point \ (S \), \ (A \) and \ (B \) operate in turn. Each operation can only select the point adjacent to the last selected point, and can not select the selected point. The person who cannot select the point loses. Ask the forerunner if he will win.

The conclusion is given first: considering all the maximum matches of bipartite graphs, if the starting point \ (S \) is included in all the maximum matching schemes, then the first hand will win, otherwise the first hand will lose.

prove:

Sufficiency: when the starting point \ (S \) is included, the first hand only needs to go along the matching edge in each step, and the next step of the second hand must go to a matching point. If you reach the unmatched point \ (p_n \), assume that the current path is \ (S\rightarrow p_1\rightarrow p_2\rightarrow...\rightarrow p_{n-1}\rightarrow p_n \), then move the matching \ (\ {S-p_1,p_2-p_3...p_{n-2}-p_{n-1} \} \) to the right to get a new matching \ (\ {p_1-p_2,p_3-p_4...p_{n-1}-p_n \} \), and the subsequent matching remains unchanged, Then this match is still the maximum match, but does not contain \ (S \), which is contrary to the fact that \ (S \) is a mandatory point.

Necessity: when the starting point \ (S \) is not included, consider a maximum matching that does not include \ (S \), and the first step must be to the matching point, otherwise this edge can also be added to the matching. Next, the later hand can continue to walk along the matching edge, and the first hand will not walk out of the matching, because assuming that the non matching point \ (q_n \) is reached, the current path is \ (S\rightarrow q_1\rightarrow q_2\rightarrow...\rightarrow q_{n-1}\rightarrow q_n \), and the matching is \ (\ {q_1-q_2,q_3-q_4...q_{n-2}-q_{n-1} \} \), move the whole right to get a new matching \ (\ {q_2-q_3,q_4-q_5...q_{n-1}-q_n \} \), and then an edge \ ((S,q_1) \) can be added to form a larger match, which is contrary to the maximum match.

In conclusion, we prove this conclusion.

Next, consider how to find all the maximum matching necessary points. We define a replacement edge as follows: \ (x \) is the unmatched point, \ ((y,z) \) is the matching edge, and there is a non matching edge between \ (x \) and \ (Y \), then connect a matching edge from \ (x \) to \ (Z \), which means that \ (x \) can replace \ (Z \). Then \ (Z \) When it becomes a non matching point, continue to find other connected edges. In this way, we first find a random maximum matching, then take all points not in the maximum matching as the starting point, and then traverse along the replacement edge. In this way, the traversed points are non necessary points, and the remaining points are necessary points.

In particular, in the title, we generally build points according to the situation.

Next are some examples:

## GYM102832H

With each revolution, the parity of the sum of all digits will change, so it conforms to the bipartite graph. Then run directly according to the above method.

## A classic question

There are multiple points on the chessboard that are not allowed to pass. You have to control one point to walk, and then you can't pass through the points that have passed, and then the first person who can't walk fails.

The sum of the abscissa and ordinate changes every time, so it is also a bipartite graph.

## A Zhengrui question

Meaning: there are multiple points on the chessboard that are not allowed to pass. You have to control the horses in two chess. You can't pass through the points that have passed. The first person who can't walk fails.

Well, it's actually no different from the above question. It's just that the code size is a little large. Throw a code and run.

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <queue>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pb(x) push_back(x)
#define MAXN 100010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
const int dx[8]={-2,-1,1,2,2,1,-1,-2};
const int dy[8]={1,2,2,1,-1,-2,-2,-1};
const bool is_print=0;
int n,m,K;
bool mp[20][20];
struct edge{
int frm,nxt,to,w;
bool is;
}ed[3000000];
void add_Edge(int u,int v,int w,bool is)
{
if(is_print) printf("%d %d\n",u,v);
ednum++;
}
int S,T,lev[MAXN],cur[MAXN],pp[MAXN];
bool bfs()
{
memset(lev,0x3f,sizeof(lev));
queue<int>que;
que.push(S),lev[S]=0;
while(!que.empty())
{
int u=que.front();que.pop();
FED(i,u)
{
if(!ed[i].w) continue;
int v=ed[i].to;
if(lev[v]>lev[u]+1)
{
lev[v]=lev[u]+1;
que.push(v);
}
}
}
if(is_print){FUP(i,1,T) printf("%d ",lev[i]);puts("");}
return lev[T]!=INF;
}
int dfs(int u,int flow)
{
if(u==T) return flow;
int re=0;
for(int &i=cur[u];i;i=ed[i].nxt)
{
int v=ed[i].to;
if(lev[v]!=lev[u]+1) continue;
int fl=dfs(v,min(flow,ed[i].w));
ed[i].w-=fl,ed[i^1].w+=fl;
flow-=fl,re+=fl;
if(!flow) break;
}
return re;
}
bool in[100000],vis[100000];
int getid(int x,int y){return x*n+y;}
void mydfs(int u)
{
if(vis[u]) return;
if(is_print) printf("mydfs :u=%d\n",u);
vis[u]=true;
FED(i,u)
{
if(ed[i].to==S||ed[i].to==T) continue;
int v=pp[ed[i].to];
mydfs(v);
}
}
namespace solve1{
void solve()
{
S=1,T=n*(n+1)+1;
FUP(x,1,n)
{
FUP(y,1,n)
{
if(mp[x][y]) continue;
int id=getid(x,y);
if((x+y)&1)
{
FUP(i,0,7)
{
int nx=x+dx[i],ny=y+dy[i];
if(nx<1||nx>n||ny<1||ny>n||mp[nx][ny]) continue;
int nid=getid(nx,ny);
}
}
}
}
int tmp=0;
while(bfs()) tmp+=dfs(S,INF);
if(is_print) printf("maxflow=%d\n",tmp);
FUP(i,1,ednum)
{
int u=ed[i].frm,v=ed[i].to;
if(u==S||u==T||v==S||v==T||ed[i].w||!ed[i].is) continue;
pp[u]=v,pp[v]=u;
in[u]=true,in[v]=true;
}
if(is_print){FUP(i,1,T) printf("i=%d in=%d pp=%d\n",i,in[i],pp[i]);}
FUP(i,2,T-1) if(!in[i]) mydfs(i);
int ans=0;
FUP(i,2,T-1) if(!vis[i]) ans++;
printf("%d\n",ans);
}
}
namespace solve2{
int getid(int x1,int y1,int x2,int y2)
{
return x1*n*n*n+y1*n*n+x2*n+y2;
}
void solve()
{
S=1,T=n*(n+1)*(n+1)*(n+1)+1;
FUP(x1,1,n)
{
FUP(y1,1,n)
{
if(mp[x1][y1]) continue;
FUP(x2,1,n)
{
FUP(y2,1,n)
{
if(mp[x2][y2]||(x1==x2&&y1==y2)) continue;
int id=getid(x1,y1,x2,y2);
if((x1+y1+x2+y2)&1)
{
FUP(i,0,7)
{
int nx=x1+dx[i],ny=y1+dy[i];
if(nx<1||nx>n||ny<1||ny>n||mp[nx][ny]||(nx==x2&&ny==y2)) continue;
int nid=getid(nx,ny,x2,y2);
}
FUP(i,0,7)
{
int nx=x2+dx[i],ny=y2+dy[i];
if(nx<1||nx>n||ny<1||ny>n||mp[nx][ny]||(nx==x1&&ny==y1)) continue;
int nid=getid(x1,y1,nx,ny);
}
}
}
}
}
}
int tmp=0;
while(bfs()) tmp+=dfs(S,INF);
if(is_print) printf("maxflow=%d\n",tmp);
FUP(i,1,ednum)
{
int u=ed[i].frm,v=ed[i].to;
if(u==S||u==T||v==S||v==T||ed[i].w||!ed[i].is) continue;
pp[u]=v,pp[v]=u;
in[u]=true,in[v]=true;
}
if(is_print){FUP(i,1,T) printf("i=%d in=%d pp=%d\n",i,in[i],pp[i]);}
FUP(i,2,T-1) if(!in[i]) mydfs(i);
int ans=0;
FUP(i,2,T-1) if(!vis[i]) ans++;
printf("%d\n",ans/2);
}
}
int main(){
FUP(i,1,K)
{