[WC2007]剪刀石頭布
阿新 • • 發佈:2019-04-28
exist using com 代碼 esp insert sin while 分享
這樣我們就可以跑費用流了
從源點連向每條邊,流量為1,費用為0
從每條邊連向兩個端點,流量為1,費用為0
從每個點連\(n-1\)條邊到\(T\),流量為\(0\sim n-2\)
最小費用最大流即可
題解
給出一個競賽圖的一些邊,你需要定向剩下的邊,使得形成的三元環最少
\(i\)如果能贏\(j\)就連一條\(i \to j\)的邊
可以發現直接統計三元環的總個數是十分困難的
我們可以考慮反面計數
\(n\)個點的競賽圖的三元環的最大個數為\(C_{n}^{3}\)
我們只需要考慮去掉每三個點不能形成三元環的條件就行了
有三條邊形不成三元環的條件就是一個點的入度/出度\(>1\)
所以如果一個點的入度為\(k\)
那麽ta的影響就是\(C_{k}^{3}\)
然後差分一下\(C_{k}^{3}-C_{k-1}^{3}=k-1\)
也就是說入度每增加\(1\)
那麽答案就會增加當前的入度
從源點連向每條邊,流量為1,費用為0
從每條邊連向兩個端點,流量為1,費用為0
從每個點連\(n-1\)條邊到\(T\),流量為\(0\sim n-2\)
最小費用最大流即可
代碼
// d[u] 表示入度 // 如果i贏了j,那麽++d[j] // 流誰誰輸 #include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int M = 105 ; const int N = 5205 ; const int INF = 1e9 ; using namespace std ; inline int read() { char c = getchar() ; int x = 0 , w = 1 ; while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; } while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; } return x*w ; } bool exist[N] ; int n , cnt , S , T ; int C[M][M] , ans , num = 1 , hea[N] ; int d[M] , sit[M][M] , id[M][M] ; int dis[N] , pre[N] ; struct E { int nxt , to , dis , cst ; } edge[N * 50] ; inline void Insert_edge(int from , int to , int dis , int cst) { edge[++num].nxt = hea[from] ; edge[num].to = to ; edge[num].dis = dis ; edge[num].cst = cst ; hea[from] = num ; } inline void add_edge(int u , int v , int w , int c) { Insert_edge(u , v , w , c) ; Insert_edge(v , u , 0 , -c) ; } inline bool spfa() { queue < int > q ; q.push(S) ; pre[T] = -1 ; memset(dis , 31 , sizeof(dis)) ; dis[S] = 0 ; while(!q.empty()) { int u = q.front() ; q.pop() ; exist[u] = false ; for(int i = hea[u] ; i ; i = edge[i].nxt) { int v = edge[i].to ; if(dis[v] > dis[u] + edge[i].cst && edge[i].dis > 0) { dis[v] = dis[u] + edge[i].cst ; pre[v] = i ; if(!exist[v]) { exist[v] = true ; q.push(v) ; } } } } return (pre[T] > 0) ; } inline void mcmf() { while(spfa()) { int diss = INF ; for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to) diss = min(diss , edge[pre[i]].dis) ; for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to) edge[pre[i]].dis -= diss , edge[pre[i] ^ 1].dis += diss ; ans -= dis[T] * diss ; } } int main() { n = read() ; S = 0 ; for(int i = 0 ; i <= n ; i ++) { C[i][0] = 1 ; for(int j = 1 ; j <= i ; j ++) C[i][j] = C[i - 1][j] + C[i - 1][j - 1] ; } ans = C[n][3] ; for(int i = 1 ; i <= n ; i ++) for(int j = 1 ; j <= n ; j ++) sit[i][j] = read() ; cnt = n ; for(int i = 1 ; i <= n ; i ++) for(int j = i + 1 ; j <= n ; j ++) { if(sit[i][j] == 1) { ++ d[j] ; ans -= d[j] - 1 ; } else if(sit[i][j] == 0) { ++ d[i] ; ans -= d[i] - 1 ; } else { ++ cnt ; id[i][j] = id[j][i] = cnt ; add_edge(S , cnt , 1 , 0) ; add_edge(cnt , i , 1 , 0) ; add_edge(cnt , j , 1 , 0) ; } } T = ++ cnt ; for(int i = 1 ; i <= n ; i ++) for(int j = d[i] + 1 ; j < n ; j ++) add_edge(i , T , 1 , j - 1) ; mcmf() ; printf("%d\n",ans) ; for(int i = 1 ; i <= n ; i ++) for(int j = i + 1 , _loser ; j <= n ; j ++) if(sit[i][j] == 2) { int u = id[i][j] ; for(int k = hea[u] ; k ; k = edge[k].nxt) { int v = edge[k].to ; if(v >= 1 && v <= n && edge[k].dis == 0) { _loser = v ; break ; } } if(_loser == i) sit[i][j] = 0 , sit[j][i] = 1 ; else sit[i][j] = 1 , sit[j][i] = 0 ; } for(int i = 1 ; i <= n ; i ++) { for(int j = 1 ; j <= n ; j ++) printf("%d ",sit[i][j]) ; printf("\n") ; } return 0 ; }
[WC2007]剪刀石頭布