[SDOI2013]森林

link

写得非常辛苦的一道题了。本来这道题其实并不难,思想理解起来也很容易,但由于我有些细节处没有搞清楚,白白浪费了一个上午来调这道题。

这道题说白了就是树上主席树(也算是树套树了?)的模板。考虑每个点维护从根到该点这条路径上的信息,查询路径时把路径拆成两个部分再差分就可以了。由于本题要支持连边,所以还需要用启发式合并来做。另外,树上主席树(动态)要设置一个0号节点,然后默认每个节点都是它的孩子;不然每个节点都建一棵权值线段树还不如打暴力。复杂度很不错,数组开到左移7位就够了。

就这么一个简单至极的东西我竟然花了两个多小时,郁闷想哭。

#include<cstdio>
#include<algorithm>
//#define zczc
using namespace std;
const int N=80010;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}
inline bool op(){
	char w=getchar();
	while(w!='Q'&&w!='L')w=getchar();
	return w=='Q';
}
inline void swap(int &s1,int &s2){
	int s3=s1;s1=s2;s2=s3;return;
}

int m,n,q,root[N];
int num,data[N],sa[N];

namespace bc{//并查集 
	int f[N],size[N];
	void init(){
		for(int i=1;i<=m;i++)f[i]=i,size[i]=1;
	}
	inline int find(int wh){
		return f[wh]==wh?wh:f[wh]=find(f[wh]);
	}
	inline void merge(int x,int y){
		int fx=find(x),fy=find(y);
		if(size[fx]<size[fy])swap(fx,fy);
		f[fy]=fx,size[fx]+=size[fy];
	}
}

namespace t{//主席树
	struct node{
		int lc,rc,l,r,size;
	}t[N<<7],newone;
	int top,st[N<<8],cnt;
	int kk(){
		return top?st[top--]:++cnt;
	}
	int kill(int wh){
		st[++top]=wh;t[wh]=newone;
	}
	void init(){
		for(int i=1;i<=cnt;i++)t[i]=newone;
		cnt=0;
	}
	int build(int l,int r){
		int wh=++cnt;t[wh].l=l,t[wh].r=r;int mid=l+r>>1;
		if(l==r)return wh;
		t[wh].lc=build(l,mid);t[wh].rc=build(mid+1,r);
		return wh;
	}
	int insert(int x,int pl){
		int wh=++cnt;t[wh]=t[x];t[wh].size++;
		if(t[wh].l==t[wh].r)return wh;int mid=t[x].l+t[x].r>>1;
		if(pl<=mid)t[wh].lc=insert(t[x].lc,pl);
		else t[wh].rc=insert(t[x].rc,pl);return wh;
	}
	int work(int x,int y,int f,int ff,int want){
		if(t[x].l==t[x].r)return sa[t[x].l];int mid=t[x].l+t[x].r>>1,lr=t[x].l;
		int sum=t[t[x].lc].size+t[t[y].lc].size-2*t[t[f].lc].size+(data[ff]<=mid&&data[ff]>=t[x].l);
		if(sum>=want)return work(t[x].lc,t[y].lc,t[f].lc,ff,want);
		else return work(t[x].rc,t[y].rc,t[f].rc,ff,want-sum);
	}
}

namespace tr{//树 
	struct edge{
		int t,next;
	}e[N<<2];
	int esum,head[N];
	inline void add(int fr,int to){
		e[++esum]=(edge){to,head[fr]};head[fr]=esum;
	}
	int lg[N],nxt[N][20],d[N];
	void init(){
		esum=0;lg[0]=1;
		for(int i=0;i<=m;i++)lg[i]=lg[i>>1]+1,d[i]=1;
		d[0]=-1;
	}
	void dfs(int wh,int fa){
		d[wh]=d[fa]+1;nxt[wh][0]=fa;
		for(int i=1;i<=lg[d[wh]];i++)nxt[wh][i]=nxt[nxt[wh][i-1]][i-1];
		root[wh]=t::insert(root[fa],data[wh]);
		for(int i=head[wh],th;i;i=e[i].next){
			if((th=e[i].t)==fa)continue;dfs(th,wh);
		}
	}
	int lca(int x,int y){
		if(d[x]<d[y])swap(x,y);
		for(int i=lg[d[x]];i>=0;i--){
			if(d[nxt[x][i]]>=d[y])x=nxt[x][i];
		}
		if(x==y)return x;
		for(int i=lg[d[x]];i>=0;i--){
			if(nxt[x][i]!=nxt[y][i])x=nxt[x][i],y=nxt[y][i];
		}
		return nxt[x][0]; 
	}
}

void worknum(){
	for(int i=1;i<=m;i++)sa[i]=data[i];
	sort(sa+1,sa+m+1);
	num=unique(sa+1,sa+m+1)-sa-1;
	for(int i=1;i<=m;i++)data[i]=lower_bound(sa+1,sa+num+1,data[i])-sa;
	return;
}

void makelink(int x,int y){
	int fx=bc::find(x),fy=bc::find(y);
	if(bc::size[fx]<bc::size[fy])swap(x,y);
	bc::merge(x,y);tr::add(x,y);tr::add(y,x);tr::dfs(y,x);
	return;
}

void enter(){
	int s1,s2,s3,lans=0;
	
	read(m);read(n);read(q);tr::d[0]=-1;
	bc::init();t::init();tr::init();
	
	for(int i=1;i<=m;i++)read(data[i]);
	worknum();root[0]=t::build(1,num);
	for(int i=1;i<=m;i++)root[i]=t::insert(root[0],data[i]);
	while(n--){read(s1);read(s2);makelink(s1,s2);}
	for(int sss=1;sss<=q;sss++){
		if(op()){
			read(s1);read(s2);read(s3);s1^=lans,s2^=lans,s3^=lans;
			int ll=tr::lca(s1,s2);
			printf("%d\n",lans=t::work(root[s1],root[s2],root[ll],ll,s3));
		}
		else{
			read(s1);read(s2);s1^=lans;s2^=lans;
			makelink(s1,s2);
		}
	}
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	int test;
	read(test);
	enter();
	
	return 0;
}
posted @ 2022-03-12 11:45  Feyn618  阅读(31)  评论(0编辑  收藏  举报