把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【LOJ6032】「雅礼集训 2017 Day2」水箱(并查集)

点此看题面

  • 一个宽为\(n\)的水箱,\(i\)\(i+1\)间存在一个高为\(a_i\)的隔板。
  • 给定\(m\)个条件,形如位置\(i\)水的高度是否达到\(h\),求最多可以同时达成多少个条件。
  • 数据组数\(\le5,n,m\le10^5\)

并查集合并

把隔板和条件全都看成"事件",按照水的高度排序,高度相同时让隔板最先、未达到次之、达到最后。

然后我们枚举水的高度,对于每个连通块记录历史上最多能同时达成的条件数目(\(g_i\))和当前达成的条件数目(\(p_i\))。

如果当前事件是隔板,只要合并两个连通块(利用并查集),\(g_i\)\(h_i\)由于之前一直是独立的,简单相加即可。

如果当前事件是未达到,因为历史最大值肯定没有达到,而当前就是指达到了当前高度,因此只要给\(g_i\)\(1\)即可。

如果当前事件是达到,给\(p_i\)\(1\),然后尝试更新\(g_i\)即可。

代码:\(O(n\alpha(n))\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,m;struct OP {int op,x,h;I bool operator < (Con OP& o) Con {return h^o.h?h<o.h:op<o.op;}}q[2*N+5];
int f[N+5],g[N+5],p[N+5];I int fa(CI x) {return f[x]^x?f[x]=fa(f[x]):x;}//并查集
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int main()
{
	RI Tt,i,x,y;read(Tt);W(Tt--)
	{
		for(read(n,m),i=1;i^n;++i) read(x),q[i]=(OP){-1,i,x};
		for(i=1;i<=m;++i) read(q[n-1+i].x,q[n-1+i].h,q[n-1+i].op);
		for(i=1;i<=n;++i) f[i]=i,g[i]=p[i]=0;//初始化并查集
		for(sort(q+1,q+n+m),i=1;i<n+m;++i) switch(q[i].op)//把事件按高度排序
		{
			case -1:x=fa(q[i].x),f[y=fa(q[i].x+1)]=x,g[x]+=g[y],p[x]+=p[y];break;//合并
			case 0:++g[fa(q[i].x)];break;case 1:x=fa(q[i].x),g[x]=max(g[x],++p[x]);break;//未达到;达到
		}printf("%d\n",g[1]),0;//最终肯定合成一个块,历史最大值即为答案
	}return 0;
}
posted @ 2021-05-10 15:58  TheLostWeak  阅读(73)  评论(0编辑  收藏  举报