ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P4382 [八省联考2018]劈配 题解

2021-11-18 17:02:43  阅读:156  来源: 互联网

标签:八省 暴力 int 题解 cnt dep head 联考 500


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

标签:八省,暴力,int,题解,cnt,dep,head,联考,500
来源: https://www.cnblogs.com/mfeitveer/p/15573181.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有