P4382 [八省聯考2018]劈配 題解
阿新 • • 發佈:2021-11-18
\(n^{2}\)過百萬,暴力出奇跡!
一道比較暴力的題目,蒟蒻也只會暴力的做法。
思路
可以一眼看出是一個二分圖,所以考慮網路流做法。
首先考慮暴力網路流。
源點向每一位選手連流量為一的邊。
每一位評委向匯點連流量為 \(b_{i}\) 的邊。
對於每一次操作都直接連邊,暴力跑網路流,來解決兩個問題。
我們發現複雜度太過巨大,可以進行一波優化。
如何優化
首先考慮第一個問題。
根據題意,我們發現,在最優情況下,每個選手“反悔”的老師只有同樣志願等級的老師。
我們很快就能想到動態連邊和動態刪邊。
我們對每一個志願等級的老師進行連邊,如果還有餘流,則記錄答案;如果沒有,則刪掉這些邊,繼續遍歷下一個志願等級的老師。
至於之前選手跑過的殘量網路不需刪除,直接跑就行了,因為需要有反悔的餘地。
當然,如果這位選手一個答案都沒找到,那麼連源點向他連得邊都可以刪掉。
接著再考慮第二個問題。
一個很容易看出的就是首先進行二分。
對於二分,我們考慮一個比較暴力的思路,記錄下之前,最優解的每一個時間(每一個選手加入的時候)的殘量網路,接著暴力加邊,跑一遍網路流,檢測有沒有餘流。
可以推算一下這個暴力做法的複雜度。
每一次二分為:\(O(\log n)\)
網路流跑二分圖為:\(O(n \sqrt{m})\)
所以第二個問題複雜度為:\(O(n^{2} \sqrt{m}\log n)\)
發現複雜度好像是一個正確的。
只能說是暴力出奇跡嗎。
Code
看著人均 \(2kb\) 的程式碼,\(4kb\) 的我陷入了沉思。
#include<bits/stdc++.h> using namespace std; const int inf = 1e7; int t , c , n , m , sl , tl , cnt = 1; int head[500] , b[500] , s[500] , cnt2[500]; int dep[500] , ans[500] , cop[500][500]; pair<int , int> a[500][500]; struct edge { int to , nxt , v; }e[10000] , ce[300][10000]; inline int read() { int asd = 0 , qwe = 1; char zxc; while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1; while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar(); return asd * qwe; } inline void add(int x , int y , int z) { e[++cnt] = (edge){y , head[x] , z} , head[x] = cnt; e[++cnt] = (edge){x , head[y] , 0} , head[y] = cnt; } inline bool bfs() { memset(dep , 0 , sizeof(dep)); queue<int> q; dep[sl] = 1 , q.push(sl); while(q.empty() == 0) { int x = q.front(); q.pop(); for(int i = head[x];i;i = e[i].nxt) { int y = e[i].to; if(dep[y] == 0 && e[i].v > 0) dep[y] = dep[x] + 1 , q.push(y); } } return dep[tl]; } inline int dfs(int now , int flow , int &mflow) { if(now == tl) { mflow += flow; return flow; } int used = 0; for(int i = head[now];i;i = e[i].nxt) { int x = e[i].to; if(e[i].v && dep[x] == dep[now] + 1) { int y = dfs(x , min(e[i].v , flow - used) , mflow); used += y , e[i].v -= y , e[i ^ 1].v += y; if(used == flow) return used; } } return used; } inline int Dinic() { int sum = 0; while(bfs()) while(dfs(sl , inf , sum)); return sum; } inline void solve1() { cnt2[0] = cnt; memset(ce[0] , 0 , sizeof(ce[0])); memset(cop[0] , 0 , sizeof(cop[0])); for(int i = 1;i <= cnt;i++) ce[0][i] = e[i]; for(int i = 1;i <= n + m + 2;i++) cop[0][i] = head[i]; for(int i = 1;i <= n;i++) { ans[i] = m + 1; int last = a[i][1].first; add(sl , i , 1); for(int j = 1;j <= m && a[i][j].first != inf;j++) { if(a[i][j].first == last) add(i , a[i][j].second + n , 1); else { int sum = Dinic(); if(sum == 1) { ans[i] = last; break; } else { cnt = cnt2[i - 1]; memcpy(e , ce[i - 1] , sizeof(ce[i - 1])); memcpy(head , cop[i - 1] , sizeof(cop[i - 1])); add(sl , i , 1) , add(i , a[i][j].second + n , 1) , last = a[i][j].first; } } } if(ans[i] == m + 1) { int sum = Dinic(); if(sum == 1) ans[i] = last; else { cnt = cnt2[i - 1]; memcpy(e , ce[i - 1] , sizeof(ce[i - 1])); memcpy(head , cop[i - 1] , sizeof(cop[i - 1])); } } memset(ce[i] , 0 , sizeof(ce[i])); memset(cop[i] , 0 , sizeof(cop[i])); cnt2[i] = cnt; for(int j = 1;j <= cnt;j++) ce[i][j] = e[j]; for(int j = 1;j <= n + m + 2;j++) cop[i][j] = head[j]; printf("%d " , ans[i]); } puts(""); } inline void calc(int x , int y) { cnt = cnt2[x - 1]; memcpy(e , ce[x - 1] , sizeof(ce[x - 1])); memcpy(head , cop[x - 1] , sizeof(cop[x - 1])); add(sl , y , 1); for(int i = 1;i <= m && a[y][i].first <= s[y];i++) add(y , a[y][i].second + n , 1); } inline void solve2() { for(int i = 1;i <= n;i++) { if(ans[i] <= s[i]) { printf("0 "); continue; } int l = 1 , r = i - 1 , sum = 0; while(l <= r) { int mid = (l + r) >> 1; calc(mid , i); int num = Dinic(); if(num == 1) l = mid + 1 , sum = mid; else r = mid - 1; } printf("%d " , i - sum); } puts(""); } int main() { t = read() , c = read(); while(t--) { n = read() , m = read() , sl = n + m + 1 , tl = n + m + 2 , cnt = 1; for(int i = 1;i <= m;i++) b[i] = read(); for(int i = 1;i <= n;i++) { for(int j = 1;j <= m;j++) { a[i][j].first = read() , a[i][j].second = j; if(a[i][j].first == 0) a[i][j].first = inf; } sort(a[i] + 1 , a[i] + m + 1); } for(int i = 1;i <= n;i++) s[i] = read(); for(int i = 1;i <= m;i++) add(i + n , tl , b[i]); solve1() , solve2(); memset(e , 0 , sizeof(e)); memset(s , 0 , sizeof(s)); memset(b , 0 , sizeof(b)); memset(cnt2 , 0 , sizeof(cnt2)); memset(head , 0 , sizeof(head)); } return 0; }