BZOJ 3123: [Sdoi2013]森林【可持久化值域线段树+启发式合并

6 篇文章 0 订阅
3 篇文章 0 订阅

裸的可持久化值域线段树+启发式(贪心又暴力地)合并

……合并的时候忘记修改深度了……不知道为什么现在稍微长一点的代码就不想调了【好颓啊QwQ

没什么好说的,一切都是套路……

求第k大/第k小很明显满足加减的性质于是

要求链上的值 其实只用记录每个节点到根的数据……然后进行加加减减【根本不用链剖【神犇们说倍增LCA太丑于是都去学LCT求LCA了我就…………跪着


#include<bits/stdc++.h>
#define MAXN 80057
#define MAX2 20000057
using namespace std;	int n,m,t;
int MX;

int cnt[MAX2],cnt_node=0;
int leftson[MAX2],rightson[MAX2];

void insert(int pre,int &now,int l,int r,int key){
	cnt[++cnt_node]=cnt[now];
	now=cnt_node;
	++cnt[now];
	if(l==r)	return ;
	int mid=(l+r)>>1;
	leftson[now]=leftson[pre] , rightson[now]=rightson[pre];
	if(key<=mid)	insert(leftson[pre],leftson[now],l,mid,key);
	else	insert(rightson[pre],rightson[now],mid+1,r,key);
} 

int inqry(int r1,int r2,int r3,int r4,		int l,int r,int k){
	if(l==r)	return l;
	int mid=(l+r)>>1;
	int dt_l=cnt[leftson[r1]]+cnt[leftson[r2]]
			-cnt[leftson[r3]]-cnt[leftson[r4]];
	if(k<=dt_l)	return inqry(leftson[r1],leftson[r2],leftson[r3],leftson[r4],l,mid,k);
	else	return inqry(rightson[r1],rightson[r2],rightson[r3],rightson[r4],mid+1,r,k-dt_l);
}

//=================================================
struct t1{
	int to,nxt;
}edge[MAXN<<1];	int cnt_edge=0;
int fst[MAXN];
void addedge(int x,int y){
	edge[++cnt_edge].to=y;
	edge[cnt_edge].nxt=fst[x];
	fst[x]=cnt_edge;
}
int rec[MAXN];

int root[MAXN];

int anc[MAXN][17];
void jump(int now){
	for(int i=1;i<17;++i)
		anc[now][i]=anc[ anc[now][i-1] ][i-1];
}

int root_num[MAXN],siz[MAXN],dpt[MAXN];
void dfs(int now){
	siz[now]=1;
	jump(now);
	insert(root[ anc[now][0] ],root[now],1,MX,rec[now]);
	for(int tmp=fst[now];tmp;tmp=edge[tmp].nxt){
		if(edge[tmp].to==anc[now][0])	continue;
		int ch=edge[tmp].to;
		anc[ch][0]=now;
		root_num[ch]=root_num[now];
		dpt[ch]=dpt[now]+1;
		
		dfs(ch);
		siz[now]+=siz[ch];
	}
}

int lca(int x,int y){
	if(dpt[x]<dpt[y])	swap(x,y);
	int dlt=dpt[x]-dpt[y];
	for(int i=16;~i;--i)
		if(dlt&(1<<i))	x=anc[x][i];
	if(x==y)	return x;
	for(int i=16;~i;--i){
		if(anc[x][i]!=anc[y][i])
			x=anc[x][i], y=anc[y][i];
	}
	if(x==y)	return x;
	return anc[x][0];
}

//============================================================
int work_Q(int x,int y,int k){
	int LCA=lca(x,y);
	int F_lca=anc[LCA][0];
	return inqry(root[x],root[y],root[LCA],root[ anc[LCA][0] ],1,MX,k);	
}

void work_L(int x,int y){
	addedge(x,y),addedge(y,x);
	anc[y][0]=x;
	siz[root_num[x]]+=siz[root_num[y]];
	root_num[y]=root_num[x];
	dpt[y]=dpt[x]+1;
	dfs(y);
}

char opt[5];
int read_x,read_y,read_k;
int lastans=0; 
int main(){
	scanf("%*d%d%d%d",&n,&m,&t);
	for(int i=1;i<=n;++i)	scanf("%d",rec+i),MX=max(MX,rec[i]);
	for(int i=1;i<=m;++i)
		scanf("%d%d",&read_x,&read_y),
		addedge(read_x,read_y), addedge(read_y,read_x);
	
	for(int i=1;i<=n;++i)
		if(!root_num[i])
			root_num[i]=i,dpt[i]=1, dfs(i);
	
	while(t--){
		scanf("%s",opt);
		if(opt[0]=='Q'){
			scanf("%d%d%d",&read_x,&read_y,&read_k);
			read_x^=lastans,read_y^=lastans,read_k^=lastans;
			printf("%d\n",lastans=work_Q(read_x,read_y,read_k));
		}
		else{
			scanf("%d%d",&read_x,&read_y);
			read_x^=lastans,read_y^=lastans; 
			if(siz[root_num[read_x]]<siz[root_num[read_y]])	swap(read_x,read_y);
			work_L(read_x,read_y);
		}	
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值