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

【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;
}
posted @ 2020-06-05 11:59  TheLostWeak  阅读(236)  评论(0编辑  收藏  举报