最小割的经典题。
这题的具体做法就不说了,网上一大堆说的很好的。
我说一下怎么理解这题里的最小割吧。
一条边如果在最小割之中,你可以理解成让这条边左边的点与右边的点矛盾所需要的代价。
下面的讨论都是限定在某一个二进制位上:
如果能让所有的点都在一个集合中那是最好的,因为所有XOR的结果都是0。
但这明显是不可能的,因为根据题目所给的已知数字,已经分成了两部分了,即这个二进制位是0还是1。
默认左边集合是1,右边集合是0。
如果一个数字只需要和左边集合XOR,那么它显然只要为1就可以了,只和右边的话显然为0。
但是有些数字是和左右都需要XOR,这里就需要做出和左边相反还是右边相反的选择了,自己画一个图,如果它需要和左边两个点XOR,右边一个点XOR,那么这个最小割显然是右边那条边。
那么就可以理解最小割了。即和最少的人相反。
再扩大点范围,就是让左边集合与右边集合相交界处的权值最小。这也和最小割的原本定义类似。
然后这题的点数好像很坑,反正记得开大点。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN = 50100;
const int MAXM = 50000;
const int INF = 0x3f3f3f3f;
struct Edge
{
int to,cap,flow,nex;
Edge() {}
Edge(int to,int cap,int flow,int nex):to(to),cap(cap),flow(flow),nex(nex) {}
} edge[MAXM];
int tol,dis[MAXN],n,m,k,head[MAXN],x[MAXN],y[MAXN],num[MAXN],ans[MAXN];
bool vis[MAXN];
void addedge(int u,int v,int cap,int rw=0)
{
edge[tol]=Edge(v,cap,0,head[u]);
head[u]=tol++;
edge[tol]=Edge(u,rw,0,head[v]);
head[v]=tol++;
}
bool bfs(int s,int t)
{
queue<int> que;
que.push(s);
memset(dis,-1,sizeof dis);
dis[s]=0;
while (!que.empty())
{
int u=que.front();
que.pop();
for (int i=head[u]; ~i; i=edge[i].nex)
{
int v=edge[i].to;
if (dis[v]==-1 && edge[i].cap>edge[i].flow)
{
dis[v] = dis[u]+1;
if (v==t) return true;
que.push(v);
}
}
}
return false;
}
int dfs(int u,int t,int cap)
{
if (u==t) return cap;
int flow=0,f;
for (int i=head[u]; ~i; i=edge[i].nex)
{
int v=edge[i].to;
if (dis[v]==dis[u]+1 && edge[i].cap>edge[i].flow)
{
f=dfs(v,t,min(cap-flow,edge[i].cap-edge[i].flow));
edge[i].flow += f;
edge[i^1].flow -=f;
flow += f;
if (flow == cap) break;
}
}
if (!flow ) dis[u]=-1;
return flow;
}
int dicnic(int s,int t)
{
int flow=0,f;
while (bfs(s,t))
while ((f=dfs(s,t,INF))>0)
flow += f;
return flow;
}
void dfs2(int u,int ad)
{
vis[u]=true;
for (int i=head[u]; ~i; i=edge[i].nex)
{
int v=edge[i].to;
if (!vis[v] && edge[i].cap>edge[i].flow)
{
ans[v] += ad;
dfs2(v,ad);
}
}
}
int main()
{
int t;
cin>>t;
while (t--)
{
memset(ans,0,sizeof ans);
memset(num,-1,sizeof num);
scanf("%d%d",&n,&m);
for (int i=1; i<=m; i++) scanf("%d%d",&x[i],&y[i]);
scanf("%d",&k);
for (int i=1; i<=k; i++)
{
int m;
scanf("%d",&m);
scanf("%d",&num[m]);
}
int ad=1;
while (1)
{
memset(vis,0,sizeof vis);
memset(head,-1,sizeof head);
tol=0;
bool flag=false;
for (int i=1; i<=n; i++)
{
if (num[i]==-1) continue;
if (num[i]>=1) flag=true;
if (num[i]&1) addedge(0,i,INF);
else addedge(i,n+1,INF);
num[i]>>=1;
}
for (int i=1; i<=m; i++) addedge(x[i],y[i],1,1);
if (!flag) break;
dicnic(0,n+1);
dfs2(0,ad);
ad<<=1;
}
for (int i=1; i<=n; i++)
{
printf("%d\n",ans[i]);
}
}
return 0;
}