SP839 Optimal marks(最小割)
阿新 • • 發佈:2018-06-09
plus ron math mat alt 建圖 name ems source
SP839 Optimal marks(最小割)
給你一個無向圖G(V,E)。 每個頂點都有一個int範圍內的整數的標記。 不同的頂點可能有相同的標記。對於邊(u,v),我們定義Cost(u,v)= mark [u] \(\oplus\) mark [v]。現在我們知道某些節點的標記了。你需要確定其他節點的標記,以使邊的總成本盡可能小。(0 < N <= 500, 0 <= M <= 3000)
先來看一下異或的性質,由於每一位是獨立的,我們可以把每一位拉出來分開考慮,變成32個子問題。
現在問題就變成了:一堆點是0,一堆點是1,一堆點沒有標號,它們互相有一些邊,一個邊的權值只當一個點是0一個點是1時才是1,否則是0。
於是我們這樣建圖:(紅色表示1,藍色表示0,白色表示沒有權值)
然後跑一個最小割即可。腦補一下,就是找出紅色點勢力和藍色點勢力的接觸處的最小邊數。sugoi!
由於題目要求點權和最小,因此我們應盡量讓紅色點最少。於是,跑完最大流以後,把從s能遍歷到的點都標成1就可以滿足紅色點最少啦!(這個不會證,留坑。)
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=505, maxm=3005, INF=1e9;
int T, n, m, k, src, dst;
inline int min(int x, int y){ return x<y?x:y; }
struct Edge{
int to, nxt, f;
}e[maxm*2+maxn], e1[maxm];
int fir[maxn], cnte;
void addedge(int x, int y, int v){
Edge &ed=e[++cnte];
ed.to=y; ed.nxt=fir[x]; ed.f=v; fir[x]=cnte; }
void RESET(){ cnte=1; memset(fir, 0, sizeof(fir)); }
int mark[maxn], gmark[maxn];
int dep[maxn], q[maxn], head, tail;
int bfs(){ //bfs來給圖分層
head=tail=0; memset(dep, 0, sizeof(dep));
dep[src]=1; q[tail++]=src; int tmp;
while (head<tail){
tmp=q[head++];
for (int i=fir[tmp]; i>0; i=e[i].nxt)
if (e[i].f>0&&!dep[e[i].to]){
dep[e[i].to]=dep[tmp]+1;
q[tail++]=e[i].to;
}
}
return dep[dst]?1:0;
}
int cur[maxn];
int dfs(int u, int flow){ //flow表示從s流到當前點的最大流量 找出一條流
if (u==dst) return flow;
if (cur[u]==-1) return 0;
for (int i=(cur[u]?cur[u]:fir[u]); i>0; i=e[i].nxt){
cur[u]=i;
if (dep[e[i].to]==dep[u]+1&&e[i].f){
int minm=dfs(e[i].to, min(flow, e[i].f));
if (minm>0){
e[i].f-=minm; e[i^1].f+=minm;
return minm;
}
}
}
cur[u]=-1;
return 0;
}
void dinic(){
while (bfs()){
memset(cur, 0, sizeof(cur));
while (dfs(src, INF));
}
}
bool vis[maxn];
void findzero(int u){
vis[u]=true;
for (int i=fir[u]; i; i=e[i].nxt){
if (vis[e[i].to]||!e[i].f) continue;
findzero(e[i].to);
}
}
int uu[maxm], vv[maxm];
int main(){
scanf("%d", &T); int t;
while (T--){
scanf("%d%d", &n, &m); dst=n+1;
for (int i=0; i<m; ++i)
scanf("%d%d", &uu[i], &vv[i]);
scanf("%d", &k);
memset(mark, 0, sizeof(mark));
memset(gmark, 0, sizeof(gmark));
for (int i=1; i<=k; ++i){
scanf("%d", &t); gmark[t]=1;
scanf("%d", &mark[t]);
}
for (int i=0; i<31; ++i){
RESET();
for (int j=0; j<m; ++j)
addedge(uu[j], vv[j], 1), addedge(vv[j], uu[j], 1);
for (int j=1; j<=n; ++j){
if (!gmark[j]) continue;
if (mark[j]&(1<<i)) addedge(src, j, INF);
else addedge(j, dst, INF);
}
dinic();
memset(vis, 0, sizeof(vis)); findzero(src);
for (int j=1; j<=n; ++j)
if (vis[j]) mark[j]|=(1<<i);
}
for (int i=1; i<=n; ++i) printf("%d\n", mark[i]);
}
return 0;
}
SP839 Optimal marks(最小割)