1. 程式人生 > >SP839 Optimal marks(最小割)

SP839 Optimal marks(最小割)

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(最小割)