题面描述:
定义无向图中的一条边的值为:这条边连接的两个点的值的异或值。
定义一个无向图的值为:这个无向图所有边的值的和。
给你一个有n个结点m条边的无向图。其中的一些点的值是给定的,而其余的点的值由你决定(但要求均为非负数),使得这个无向图的值最小。在无向图的值最小的前提下,使得无向图中所有点的值的和最小。
分析:
看到异或,当然先想到了线性积,但是由于有不确定元素,就想到了这道题
对付异或,一个有力的方法就是拆位
异或的运算法则:相同为0,不同为1
只有这一位是1的时候才会产生代价
实际上就相当于把这些点分配到两个集合中,点之间有关联关系
如果有关联的点在同一个集合,就不会产生代价,否则会产生
2i
2
i
的贡献
舒老师说这是ISA讲过的集合划分经典题
建图如下:
枚举二进制的每一位
i
i
填源S加汇T,这一位是1的点和S连单向边,容量为INF(保证不会割掉这条边)
这一位是0的点向T连单向边,容量为INF
有关联关系的点对之间连双向边,容量为1
求得最小割,那么代价即为
X∗2i
X
∗
2
i
(如果割掉了这条边,说明这条边链接的两个点难免要分配到两个集合中从而产生代价)
跑完最大流后,在残量网络中dfs寻找S集合,也就是这些点都属于1
tip
注意数据范围
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int INF=0x33333333;
const int N=30010;
struct node{
int y,nxt,v;
};
node way[2000010];
int st[N],tot,s,t,n,m,K,x[N],y[N],id[N],ans[N];
int deep[N];
void add(int u,int w,int z) {
tot++;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
}
int bfs(int s,int t) {
memset(deep,-1,sizeof(deep));
deep[s]=1;
queue<int> q;
q.push(s);
while (!q.empty()) {
int now=q.front(); q.pop();
for (int i=st[now];i!=-1;i=way[i].nxt)
if (way[i].v&&deep[way[i].y]==-1) {
deep[way[i].y]=deep[now]+1;
q.push(way[i].y);
}
}
return deep[t]!=-1;
}
int dfs(int now,int t,int limit) {
if (now==t||!limit) return limit;
int f,flow=0;
for (int i=st[now];i!=-1;i=way[i].nxt)
if (way[i].v&&deep[way[i].y]==deep[now]+1&&(f=dfs(way[i].y,t,min(limit,way[i].v)))) {
flow+=f;
limit-=f;
way[i].v-=f;
way[i^1].v+=f;
if (!limit) break;
}
return flow;
}
void doit(int s,int t) {
int ans=0;
while (bfs(s,t))
ans+=dfs(s,t,INF);
}
void ss(int now,int o) {
for (int i=st[now];i!=-1;i=way[i].nxt)
if (way[i].v&&deep[way[i].y]==0) {
deep[way[i].y]=1;
ans[way[i].y]|=o;
ss(way[i].y,o);
}
}
void solve() {
bool flag=1;
s=0,t=n+1;
for (int _=0;_<=31;_++) {
memset(st,-1,sizeof(st)); tot=-1;
for (int i=1;i<=K;i++)
if ((ans[id[i]]>>_)&1) add(s,id[i],INF),add(id[i],s,0);
else add(id[i],t,INF),add(t,id[i],0);
for (int i=1;i<=m;i++)
add(x[i],y[i],1),add(y[i],x[i],1);
doit(s,t);
memset(deep,0,sizeof(deep));
deep[s]=1; ss(s,1<<_);
}
}
int main()
{
int T;
scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d",&x[i],&y[i]);
scanf("%d",&K);
memset(ans,0,sizeof(ans));
for (int i=1;i<=K;i++) {
int x,y;
scanf("%d%d",&x,&y);
ans[x]=y;
id[i]=x;
}
solve();
for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
return 0;
}