【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;
}
待到再迷茫时回头望,所有脚印会发出光芒