bzoj 3123: [Sdoi2013]森林 (主席树+启发式合并+并查集)

3123: [Sdoi2013]森林

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 2495   Solved: 747
[ 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

HINT



对于第一个操作 Q 8 7 3,此时 lastans=0,所以真实操作为Q 8^0 7^0 3^0,也即Q 8 7 3。点8到点7的路径上一共有5个点,其权值为4 1 1 2 4。这些权值中,第三小的为 2,输出 2,lastans变为2。对于第二个操作 Q 3 5 1 ,此时lastans=2,所以真实操作为Q 3^2 5^2 1^2 ,也即Q 1 7 3。点1到点7的路径上一共有4个点,其权值为 1 1 2 4 。这些权值中,第三小的为2,输出2,lastans变为 2。之后的操作类似。 

 

Source

[ Submit][ Status][ Discuss]

题解:主席树+启发式合并+并查集

路径上第k大,是树上主席树的经典问题。

我们在树上主席树的基础上需要做的就是启发式合并,其实就是每次讲小一点的树暴力重建到大树上。

码力题。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 160003
using namespace std;
int tot,sz,n,m,q;
int root[N],point[N],next[N*2],v[N*2],belong[N],ff[N][20],fa[N],num[N],b[N],val[N],c[N];
int deep[N],mi[20],vis[N],cnt,q1[N],size[N];
struct data
{
	int l,r,w;
}tr[40000000];
void init()
{
	tot=0; sz=0; deep[0]=0;
	memset(point,0,sizeof(point));
	tr[0].w=tr[0].l=tr[0].r=0;
	memset(fa,0,sizeof(fa));
	memset(vis,0,sizeof(vis));
	memset(root,0,sizeof(root));
}
void add(int x,int y)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}
int cmp(int x,int y)
{
	return val[x]<val[y];
}
void insert(int &i,int l,int r,int pos)
{
	tr[++sz]=tr[i]; i=sz;
	tr[i].w++;
	if (l==r) return;
	int mid=(l+r)/2;
	if (pos<=mid) insert(tr[i].l,l,mid,pos);
	else insert(tr[i].r,mid+1,r,pos);
}
int query(int l,int r,int k,int x,int y,int z,int d)
{
	if (l==r) return l; 
	int t=tr[tr[x].l].w+tr[tr[y].l].w-tr[tr[z].l].w-tr[tr[d].l].w;
	int mid=(l+r)/2;
	if (t>=k) return query(l,mid,k,tr[x].l,tr[y].l,tr[z].l,tr[d].l);
	else return query(mid+1,r,k-t,tr[x].r,tr[y].r,tr[z].r,tr[d].r);
}
void dfs(int x,int f)
{
	vis[x]=1; deep[x]=deep[f]+1;
	for (int i=1;i<=17;i++){
		if (deep[x]-mi[i]<0) ff[x][i]=0;
		ff[x][i]=ff[ff[x][i-1]][i-1];
	}
	root[x]=root[f];
	insert(root[x],1,n,c[x]);
	for (int i=point[x];i;i=next[i]) {
		if (vis[v[i]]||v[i]==f) continue;
		fa[v[i]]=x; ff[v[i]][0]=x;
		dfs(v[i],x);
	}
}
void solve(int x,int f,int dep)
{
	fa[x]=f; ff[x][0]=f; deep[x]=dep;
	for (int i=1;i<=17;i++){
		if (deep[x]-mi[i]<0) ff[x][i]=0;
		ff[x][i]=ff[ff[x][i-1]][i-1];
	}
	root[x]=root[f];
	insert(root[x],1,n,c[x]);
	for (int i=point[x];i;i=next[i]) {
		if (v[i]==f) continue;
		ff[v[i]][0]=x;
		solve(v[i],x,dep+1);
	}
}
int find(int x)
{
	if (belong[x]==x) return x;
	belong[x]=find(belong[x]);
	return belong[x];
}
int lca(int x,int y)
{
	if (deep[x]<deep[y]) swap(x,y);
	int k=deep[x]-deep[y];
	for (int i=0;i<=17;i++)
	 if (k>>i&1) x=ff[x][i];
	if (x==y) return x;
	for (int i=17;i>=0;i--)
	 if (ff[x][i]!=ff[y][i]) 
	  x=ff[x][i],y=ff[y][i];
	return ff[x][0];
}
int main()
{
	freopen("rforest.in","r",stdin);
	freopen("rforest.out","w",stdout);
	int T;
	scanf("%d",&T); T=1;
	mi[0]=1;
	for (int i=1;i<=17;i++) mi[i]=mi[i-1]*2;
	for(int t=1;t<=T;t++) {
		init();
		scanf("%d%d%d",&n,&m,&q);
		for (int i=1;i<=n;i++) scanf("%d",&val[i]),b[i]=i;
		sort(b+1,b+n+1,cmp);
		for (int i=1;i<=n;i++) c[b[i]]=i,num[i]=val[b[i]];
		for (int i=1;i<=n;i++) belong[i]=i,size[i]=1;
		for (int i=1;i<=m;i++) {
			int x,y; scanf("%d%d",&x,&y);
			add(x,y); 
			int r1=find(x); int r2=find(y);
			if (size[r1]<size[r2]) swap(r1,r2);
			belong[r2]=r1; size[r1]+=size[r2]; size[r2]=0;
		}
		for (int i=1;i<=n;i++)
		 if (!vis[i])  dfs(i,0);
		int ans=0;
		for (int i=1;i<=q;i++) {
			char s[10]; int x,y,k;
			scanf("%s%d%d",s,&x,&y); x^=ans; y^=ans;
			if (s[0]=='L'){
				cnt=0;
				int r1=find(x); int r2=find(y); 
				if (size[r1]>size[r2]) swap(x,y),swap(r1,r2);
				belong[r1]=r2; size[r2]+=size[r1];
				solve(x,y,deep[y]+1);
				add(x,y);
			}
			if (s[0]=='Q'){
				scanf("%d",&k); k^=ans;
				int t=lca(x,y);
				ans=query(1,n,k,root[x],root[y],root[t],root[fa[t]]);
				ans=num[ans];
				printf("%d\n",ans);
			}
		}
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值