BZOJ3123: [Sdoi2013]森林

题解:在树上转移主席树 每次连边暴力倍增维护 每个节点维护到根路径上的权值情况即可

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define link(x) for(edge *j=h[x];j;j=j->next)
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
const int MAXN=8e4+10;
const double eps=1e-8;
#define ll long long
using namespace std;
struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int fa[MAXN],f[MAXN][21],sz,num[MAXN],cnt,a[MAXN],n,m,q;
int rt[MAXN],size[MAXN];
bool vis[MAXN];
int find1(int x){
	if(x==fa[x])return x;
	else return fa[x]=find1(fa[x]);
}
typedef struct node{
	int l,r,sum;
}node;
node d[MAXN*404];
vector<int>vec;
void update(int &x,int y,int l,int r,int t){
	x=++cnt;d[x]=d[y];d[x].sum++;
	//cout<<l<<" "<<r<<" "<<t<<endl;
	if(l==r)return ;
	int mid=(l+r)>>1;
	if(t<=mid)update(d[x].l,d[y].l,l,mid,t);
	else update(d[x].r,d[y].r,mid+1,r,t);
}
int ans;int dep[MAXN];
void querty(int x,int y,int t1,int t2,int l,int r,int k){
	if(l==r){ans=l;return ;}
	int mid=(l+r)>>1;
	int t=d[d[x].l].sum+d[d[y].l].sum-d[d[t1].l].sum-d[d[t2].l].sum;
	if(k<=t)querty(d[x].l,d[y].l,d[t1].l,d[t2].l,l,mid,k);
	else querty(d[x].r,d[y].r,d[t1].r,d[t2].r,mid+1,r,k-t);
}
void dfs(int x,int pre,int deep){
	//cout<<x<<"====="<<endl;
	num[x]=1;f[x][0]=pre;vis[x]=1;dep[x]=deep+1;
	link(x){
		if(j->t!=pre)dfs(j->t,x,deep+1),num[x]+=num[j->t];
	}
}
void dfs1(int x){
	//cout<<x<<"===="<<endl;
	update(rt[x],rt[f[x][0]],1,sz,a[x]);
	for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1];
	link(x){
		if(j->t!=f[x][0])dfs1(j->t);
	}
}
int Lca(int u,int v){
	if(dep[u]<dep[v])swap(u,v);
	int tmp=dep[u]-dep[v];
	for(int i=0;i<=20;i++)if(tmp&(1<<i))u=f[u][i];
	if(u==v)return u;
	for(int i=20;i>=0;i--){
		if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i];
	}
	return f[u][0];
}
int main(){
	read();n=read();m=read();q=read();
	inc(i,1,n)fa[i]=i,a[i]=read(),vec.pb(a[i]);
	sort(vec.begin(),vec.end());
	sz=unique(vec.begin(),vec.end())-vec.begin();
	inc(i,1,n)a[i]=lower_bound(vec.begin(),vec.begin()+sz,a[i])-vec.begin()+1;
	int u,v;
	inc(i,1,m){
		u=read();v=read();add(u,v);add(v,u);
		int t1=find1(u);int t2=find1(v);
		if(t1<t2)swap(t1,t2);
		fa[t1]=t2;
	}
	inc(i,1,n)if(!vis[i])dfs(i,0,0),dfs1(i);
	inc(i,1,n)size[i]=num[i];
	char ch;int res=0,k;
	while(q--){
		scanf(" %c",&ch);u=read()^res;v=read()^res;
		//cout<<u<<" "<<v<<endl;
		if(ch=='L'){
			int t1=find1(u);int t2=find1(v);
			if(size[t1]>size[t2])swap(t1,t2),swap(u,v);
			fa[t1]=t2;size[t2]+=size[t1];dfs(u,v,dep[v]);dfs1(u);
			add(u,v);add(v,u);
		}
		else{
			k=read()^res;int lca=Lca(u,v);
			querty(rt[u],rt[v],rt[lca],rt[f[lca][0]],1,sz,k);res=vec[ans-1];
			printf("%d\n",res);
		}
	}
	return 0;
}

  

3123: [Sdoi2013]森林

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 4658  Solved: 1371
[Submit][Status][Discuss]

Description

 

Input

第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1≤testcase≤20。 
第二行包含三个整数N,M,T,分别表示节点数、初始边数、操作数。第三行包含N个非负整数表示 N个节点上的权值。 
 接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分。

Output

对于每一个第一类操作,输出一个非负整数表示答案。 
 
 

Sample Input

1
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3 Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 L 0 7
Q 9 2 5 Q 6 1 6

Sample Output

2
2
1
4
2

 

posted @ 2018-09-28 10:34  wang9897  阅读(114)  评论(0编辑  收藏  举报