【BZOJ4399】魔法少女LJJ(线段树合并)
作者:互联网
大致题意: 要求支持\(7\)种操作:新建一个权值为\(x\)的点;连接两个点;把\(a\)所在连通块小于\(x\)的点权修改为\(x\);把\(a\)所在连通块大于\(x\)的点权修改为\(x\);询问\(a\)所在连通块第\(k\)小的点权;询问\(a\)与\(b\)所在连通块乘积的大小关系;询问\(a\)所在连通块点数。
前言
其实原题面里还有两个操作:断开一条边;删去一个点。
一眼看上去似乎很不可做?
然而,表示操作种类的\(c\)的范围是\(c\le7\)。。。
线段树合并
考虑连接两个点其实就是合并两个连通块信息,然后又有询问\(k\)大值的操作,容易想到权值线段树合并。
修改操作其实就是求出权值小于(大于)\(x\)的点数,将这一部分清零,并修改权值为\(x\)的点的数量。
而比较乘积,一个套路的做法就是把所有数取个\(log\),这样乘法变加法,就可以维护并比较了。
代码
#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 400000
#define LN 20
#define DB double
using namespace std;
int n,f[N+5],Rt[N+5],dc,dv[N+5];struct Op {int op,x,y;}q[N+5];
I int fa(CI x) {return f[x]?f[x]=fa(f[x]):x;}
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define D isdigit (c=tc())
int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('\n');}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
class SegmentTree
{
private:
#define LT l,mid,O[rt].S[0]
#define RT mid+1,r,O[rt].S[1]
#define PU(x) (O[x].G=O[O[x].S[0]].G+O[O[x].S[1]].G,O[x].Sz=O[O[x].S[0]].Sz+O[O[x].S[1]].Sz)
#define PD(x) O[x].F&&(C(O[x].S[0]),C(O[x].S[1]),O[x].F=0)
#define C(x) (O[x].G=O[x].Sz=0,O[x].F=1)
int Nt;struct node {DB G;int Sz,F,S[2];}O[N*LN<<1];
public:
I DB operator [] (CI x) {return O[x].G;}
I void U(CI x,CI v,CI l,CI r,int& rt)//修改点权为x的点数为v
{
if(!rt&&(rt=++Nt),l==r) return (void)(O[rt].G=(O[rt].Sz=v)*log(dv[x]));PD(rt);
int mid=l+r>>1;x<=mid?U(x,v,LT):U(x,v,RT),PU(rt);
}
I void Merge(int& x,CI y)//线段树合并
{
if(!x||!y) return (void)(x|=y);PD(x),PD(y),O[++Nt]=O[x],O[x=Nt].G+=O[y].G,
O[x].Sz+=O[y].Sz,PD(x),PD(y),Merge(O[x].S[0],O[y].S[0]),Merge(O[x].S[1],O[y].S[1]);
}
I int Gsz(CI L,CI R,CI l,CI r,CI rt)//求点权在[L,R]范围内的点数
{
if(L<=l&&r<=R) return O[rt].Sz;int mid=l+r>>1;PD(rt);
return (L<=mid?Gsz(L,R,LT):0)+(R>mid?Gsz(L,R,RT):0);
}
I void Cl(CI L,CI R,CI l,CI r,CI rt)//清空[L,R]范围内的信息
{
if(L<=l&&r<=R) return (void)C(rt);int mid=l+r>>1;PD(rt),
L<=mid&&(Cl(L,R,LT),0),R>mid&&(Cl(L,R,RT),0),PU(rt);
}
I int GV(CI k,CI l,CI r,CI rt)//求k大值
{
if(l==r) return dv[l];int mid=l+r>>1;PD(rt);
return O[O[rt].S[0]].Sz>=k?GV(k,LT):GV(k-O[O[rt].S[0]].Sz,RT);
}
}S;
int main()
{
RI Qt,i,x,y;for(F.read(Qt),i=1;i<=Qt;++i) F.read(q[i].op,q[i].x),q[i].op^7&&//读入并存储下询问
(q[i].op^1?F.read(q[i].y),(q[i].op==3||q[i].op==4)&&(dv[++dc]=q[i].y):dv[++dc]=q[i].x);
for(sort(dv+1,dv+dc+1),dc=unique(dv+1,dv+dc+1)-dv-1,i=1;i<=Qt;++i) switch(q[i].op)
{
case 1:S.U(lower_bound(dv+1,dv+dc+1,q[i].x)-dv,1,1,dc,Rt[++n]);break;//新建点,注意离散化
case 2:(x=fa(q[i].x))^(y=fa(q[i].y))&&(S.Merge(Rt[x],Rt[y]),f[y]=x);break;//不在同一连通块中就合并
case 3:q[i].y=lower_bound(dv+1,dv+dc+1,q[i].y)-dv,
x=S.Gsz(1,q[i].y,1,dc,Rt[q[i].x=fa(q[i].x)]),S.Cl(1,q[i].y,1,dc,Rt[q[i].x]),//求出点数,并清空
S.U(q[i].y,x,1,dc,Rt[q[i].x]);break;//修改
case 4:q[i].y=lower_bound(dv+1,dv+dc+1,q[i].y)-dv,
x=S.Gsz(q[i].y,dc,1,dc,Rt[q[i].x=fa(q[i].x)]),S.Cl(q[i].y,dc,1,dc,Rt[q[i].x]),//求出点数,并清空
S.U(q[i].y,x,1,dc,Rt[q[i].x]);break;//修改
case 5:F.writeln(S.GV(q[i].y,1,dc,Rt[fa(q[i].x)]));break;//询问k大值
case 6:F.writeln(S[Rt[fa(q[i].x)]]>S[Rt[fa(q[i].y)]]?1:0);break;//比较log大小
case 7:F.writeln(S.Gsz(1,dc,1,dc,Rt[fa(q[i].x)]));break;//询问连通块点数
}return F.clear(),0;
}
标签:rt,CI,LJJ,int,线段,连通,FS,BZOJ4399,define 来源: https://www.cnblogs.com/chenxiaoran666/p/BZOJ4399.html