HDU5575 Discover Water Tank(启发式合并堆,2015ICPC上海)

题意:

给出一些水槽。

用一些高度不同的隔板把水槽隔开。

在水槽的不同位置和不同高度,有一些探测器,探测器可能返回有水或没水。

询问是否存在一种放水方案,使得尽可能多的探测器说真话。

放水方案需满足连通器原理。

做法:

从小到大枚举隔板,将隔板两侧的集合合并,对每个集合维护:
1)f[i]:表示这个集合内部所有水槽全部溢出的情况下的最优解。
2)g[i]:表示这个集合内部的水槽有些还被分隔的情况下的最优解。
合并的过程中,从小到大枚举两边集合内部的探测器,模拟水位的上升,得出最优水位时的情况和完全溢出的情况对答案的影响,同时把已经被水位覆盖的探测器取出。
设t1,t2为完全溢出后的影响,设sum1,sum2为某个水位下的最佳情况的影响。
有:
ft=f[L]+f[R]+t1+t2;
gt=max(g[L],f[L]+sum1)+max(g[R],f[R]+sum2);

ft和gt为合并后的f值和g值。

注意,最后整个水槽完全溢出后,可能还有一些上方的探测器,需要再做一遍模拟。

每次取最小值这个需求,用优先队列即可。

每次合并两个队列,网上很多题解采用左偏树的方法,这里我采用的是启发式合并,好写很多。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;

priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q[maxn];
int father[maxn];
int findfather (int x) {
	int a=x;
	while (x!=father[x]) x=father[x];
	while (a!=father[a]) {
		int z=a;
		a=father[a];
		father[z]=x;
	} 
	return x;
}
int _,n,m;
int h[maxn],p[maxn];
int g[maxn];//这个集合部分不合并的最大值
int f[maxn];//这个集合全部合并的最大值 
int sz[maxn];
int main () {
	scanf("%d",&_);
	for (int k=1;k<=_;k++) {
		scanf("%d%d",&n,&m);
		for (int i=1;i<=n;i++) father[i]=i,sz[i]=0;
		for (int i=1;i<=n;i++) while (q[i].size()) q[i].pop();
		for (int i=1;i<n;i++) scanf("%d",h+i),p[i]=i;
		int ans=0;
		for (int i=1;i<=m;i++) {
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			if (z==0) sz[x]++;
			y++;
			q[x].push({y,z});
		}
		for (int i=1;i<=n;i++) f[i]=sz[i],g[i]=sz[i];//初始化未合并的值 
		sort(p+1,p+n,[&](int x,int y) {
			return h[x]<h[y];
		});
		for (int i=1;i<n;i++) {
			int sum1=0,t1=0;
			int L=findfather(p[i]);
			int R=findfather(p[i]+1);
			while (q[L].size()) {
				if (q[L].top().first>h[p[i]]) break;
				if (q[L].top().second==0) {
					t1--;
				}
				else {
					t1++;
				}
				sum1=max(sum1,t1);
				q[L].pop();
			}
			int sum2=0,t2=0;
			while (q[R].size()) {
				if (q[R].top().first>h[p[i]]) break;
				if (q[R].top().second==0) {
					t2--;
				}
				else {
					t2++;
				}
				sum2=max(sum2,t2);
				q[R].pop();
			}
			//printf("%d %d\n",sum1,sum2);
			int ft,gt;//合并后新的根的f值、g值
			ft=f[L]+f[R]+t1+t2;
			gt=max(g[L],f[L]+sum1)+max(g[R],f[R]+sum2);
				
			if (q[L].size()<q[R].size()) {
				father[L]=R;
				while (q[L].size()) q[R].push(q[L].top()),q[L].pop();
				f[R]=ft;
				g[R]=gt;
			}
			else {
				father[R]=L;
				while (q[R].size()) q[L].push(q[R].top()),q[R].pop();
				f[L]=ft;
				g[L]=gt;
			}
		}
		int u=findfather(1);
		int sum1=0,t1=0;
		while (q[u].size()) {
			if (q[u].top().second==0) t1--;
			else t1++;
			sum1=max(sum1,t1);
			q[u].pop();
		}
		
		ans=max(f[u]+sum1,g[u]);
		printf("Case #%d: %d\n",k,ans);
	}
}
posted @ 2021-10-14 23:49  zlc0405  阅读(54)  评论(0编辑  收藏  举报