1. 程式人生 > 其它 >P4382 [八省聯考2018]劈配 題解

P4382 [八省聯考2018]劈配 題解

\(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;
}