转载自:http://blog.youkuaiyun.com/baidu_23081367/article/details/52403163
题目大意:
给定一张图,每个点有一个数字,有的点没有数字。
给所有点涂上数字,使得任意直接相连的2个点的数字,xor值的和最小。
对于xor运算,每一位之间没有关系。所以题目首先拆分成31个小问题。。求最后每个数字的每一位(二进制位)的数字。
然后目前的问题就是,一张图,很多点数字是0,很多点数字是1,还有一些点没有上数字。 找一个方案,使得任何直接相连的点的数字,xor值的和最小。
显然0附近的点都写0比较好,1附近的点都写1比较好。 最后1和0交汇了,希望交汇的点尽量小。 这显然就是一个最小割。然后新原连1的边,0连新汇,流量都是无穷大。其他原来图中相连的边,都是正反连上流量为1的边即可。跑一个最大流,割的位置,就是01交汇的地方。然后从S出发经过的所有点,都涂上1. 其他点都是0.
代码
#include<stdio.h>
#include<string.h>
#define INF 0x3fffffff
#define NN 504
#define MM 8010
typedef struct node{
int v, w;
struct node *nxt, *op;
}NODE;
NODE edg[MM];
NODE *Link[NN];
int h[NN];
int num[NN]; // gap优化,标号为i的顶点个数
int cnt[NN];
int vis[NN];
int mark[NN];
int x[3004], y[3004];
int M, N, idx, S, T, n; // S 表示源点,T表示汇点,n表示节点个数
void Add(int u, int v, int c1, int c2){
idx++; //idx记得初始化,不然很容易栈溢出
edg[idx].v = v;
edg[idx].w = c1;
edg[idx].nxt = Link[u];
edg[idx].op = edg + idx + 1;
Link[u] = edg + idx;
idx++;
edg[idx].v = u;
edg[idx].w = c2; // 有向边为0,无向边为c
edg[idx].nxt = Link[v];
edg[idx].op = edg + idx - 1;
Link[v] = edg + idx;
}
int Min(int a, int b){
return a < b ? a : b;
}
int aug(int u, int flow){
if (u == T) return flow;
int l = flow; // l表示剩余容量
int tmp = n - 1;
for (NODE *p = Link[u]; p; p = p->nxt){
if (h[u] == h[p->v] + 1 && p->w){
int f = aug(p->v, Min(l, p->w));
l -= f;
p->w -= f;
p->op->w += f;
if (l == 0 || h[S] == n) return flow - l; // gap
}
if (p->w > 0 && h[p->v] < tmp){
tmp = h[p->v];
}
}
if(l == flow){
num[h[u]]--; // gap
if (num[h[u]] == 0) h[S] = n; // gap,每个点的距离值最多为n - 1,这里设为n 表示断层了
else{
h[u] = tmp + 1;
num[h[u]]++; // gap
}
}
return flow - l;
}
void Init(){
idx = 0;
S = 0;
T = N + 1;
n = T + 1;
memset(Link, 0, sizeof(Link));
}
/*n表示总点的个数,包括源点和汇点*/
int sap(){
int ans = 0;
memset(h, 0, sizeof(h));
memset(num, 0, sizeof(num));
num[0] = n;
while(h[S] < n){
ans += aug(S, INF);
}
return ans;
}
void dfs(int u, int base){// 所有能搜到的点,都是当前位为1的点
cnt[u] += base;
for (NODE *p = Link[u]; p; p = p->nxt){
if (p->w && !vis[p->v]){
vis[p->v] = 1;
dfs(p->v, base);
}
}
}
void Solve(){
int flag, base, i;
flag = 1;
base = 1;
memset(cnt, 0, sizeof(cnt));// 记录每个点的最后标号
while(flag){// 对每一位作处理,一直到最高位为0为止
flag = 0;
Init();
for (i = 1; i <= M; i++){
Add(x[i], y[i], 1, 1);
}
for (i = 1; i <= N; i++){
if (mark[i] != -1){
if (mark[i] >= 1){
flag = 1;
}
if (mark[i] % 2){
Add(S, i, INF, 0);
}else{
Add(i, T, INF, 0);
}
mark[i] /= 2;
}
}
sap();
memset(vis, 0, sizeof(vis));
vis[S] = 1;
dfs(S, base);
base *= 2;
}
for (i = 1; i <= N; i++){
printf("%d\n", cnt[i]);
}
}
int main()
{
int iT, i, K, u, p;
scanf("%d", &iT);
while(iT--){
scanf("%d%d", &N, &M);
for(i = 1; i <= M; i++){
scanf("%d%d", &x[i], &y[i]);
}
scanf("%d", &K);
memset(mark, -1, sizeof(mark));
for (i = 1; i <= K; i++){
scanf("%d%d", &u, &p);
mark[u] = p;
}
Solve();
}
return 0;
}