ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[SDOI2013] 森林(主席树+启发式合并)

2022-07-13 12:03:34  阅读:159  来源: 互联网

标签:rs int 合并 mid ecnt fa SDOI2013 ls 启发式


传送门

description

给一个森林,每个点有权值。在线(每次每个变量xor lastans)
操作如下:

  • Q x y k:查询x到y的路径上第k小的权值(保证x到y至少k个点)
  • L x y:将x和y连边。

solution

从Q和L分别可以猜测需要用到主席树和lct。

主席树是我一直不太熟的数据结构,作用可以维护一段历史版本的方案(每一个历史版本都是上一个版本加简单增量而来的),查询一段版本则是差分。
一般线性表,就从左往右依次加入(利用上一个版本加上增量,增量一般新建log级别的节点)

首先不会虽然还可做的lct……
询问:可以想象为主席树,每个节点维护到根的各个权值的个数。每个节点可以由父亲版本推到,因此insert操作也是很好写的。
还需要倍增求lca。路径某权值个数为:两端到根的和-lca到根-lca的父亲到根。
操作:加边等同于合并两个连通块。用并查集维护连通块以及大小。然后发现主席树这种东西不好修改,一次只能对一个节点的版本改。多个节点,不就是暴力了吗?
优化暴力,用启发式合并即可。时空复杂度\(O(nlog^2n)\)
具体就dfs轻的连通块里每一个点,暴力更新(父亲,dep,倍增数组,对应主席树版本)。

*ps.70pt的注意:dfs中更新节点的倍增数组时要更新到\(2^i>=n\)而不是\(2^i>=dep\),因为是动态图,之前的\(dep\)可能比现在大的,所以没更新到的本来应该默认为\(0\),但此时可能非\(0\),要再更一下(相当于清空)。这题直接更到\(16\)就行了。

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
const int M=1e7+5;
int fa[N],sz[N],nd,val[N],lg[N],f[N][21],dep[N];
int g_fa(int u) {return fa[u]==u?u:fa[u]=g_fa(fa[u]);} 
struct seg {int cnt,ls,rs;}T[M];
int rt[N],n,m,nxt[N],to[N],head[N],ecnt,a[N];
void add_edge(int u,int v) {
	nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;
	nxt[++ecnt]=head[v];to[ecnt]=u;head[v]=ecnt;
}
int LCA(int x,int y) {
	if(dep[x]<dep[y]) {swap(x,y);}
	int k=dep[x]-dep[y];for(int i=0;i<=lg[k];i++)if((1<<i)&k)x=f[x][i];
	if(x==y)return x;
	for(int i=lg[dep[x]];i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	return f[x][0];
}
void Build(int &x,int l,int r) {		//Build(rt[0])用这样每个节点都能指向它有完整的左右子树信息 
	x=++nd;
	if(l==r)return;
	int mid=(l+r)>>1;
	Build(T[x].ls,l,mid),Build(T[x].rs,mid+1,r);
}
void Insert(int &x,int fx,int l,int r,int v) {
	x=++nd;
	T[x]=T[fx]; T[x].cnt++;
	if(l==r)return;
	int mid=(l+r)>>1;
	(v<=mid)?Insert(T[x].ls,T[fx].ls,l,mid,v):Insert(T[x].rs,T[fx].rs,mid+1,r,v);
}

void dfs(int x,int fx) {
	dep[x]=dep[fx]+1;
	f[x][0]=fx;	
	for(int i=1;i<=16;i++) {f[x][i]=f[f[x][i-1]][i-1];}		//必须更到 16,有一些虽然到不了,但要赋0(clear\kk) 
	Insert(rt[x],rt[fx],1,m,a[x]);
	for(int i=head[x];i;i=nxt[i]) {
		int y=to[i];if(y==fx)continue;
		dfs(y,x);
	}
}

int lans;
void Query(int x,int y,int fx,int fy,int l,int r,int k) {	//x,y,lca,fa[lca]
	if(l==r) {lans=val[l];return;}
	int w=T[T[x].ls].cnt+T[T[y].ls].cnt-T[T[fx].ls].cnt-T[T[fy].ls].cnt,mid=(l+r)>>1;
	if(w>=k) {Query(T[x].ls,T[y].ls,T[fx].ls,T[fy].ls,l,mid,k);}
	else {Query(T[x].rs,T[y].rs,T[fx].rs,T[fy].rs,mid+1,r,k-w);}
}
int main() {
//	freopen("P3302_6.in","r",stdin);
//	freopen("ans.out","w",stdout);
	int testcase;scanf("%d",&testcase);
	int cm,T;scanf("%d%d%d",&n,&cm,&T);
	lg[1]=0;for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
	for(int i=1;i<=n;i++) {scanf("%d",&a[i]);val[i]=a[i];}
	sort(val+1,val+1+n);m=unique(val+1,val+1+n)-val-1;
	for(int i=1;i<=n;i++) {sz[i]=1;fa[i]=i;a[i]=lower_bound(val+1,val+1+m,a[i])-val;}
	
	for(int i=1;i<=cm;i++) {
		int x,y;scanf("%d%d",&x,&y);add_edge(x,y);
		int u=g_fa(x),v=g_fa(y);
		if(u==v)continue;
		sz[v]+=sz[u];fa[u]=v;
	}
	Build(rt[0],1,m);
	for(int i=1;i<=n;i++) {
		if(g_fa(i)==i) {dfs(i,0);}
	}
	for(int i=1;i<=T;i++) {
		char ch[3];int x,y,k;
		scanf("%s%d%d",ch,&x,&y);
		x^=lans,y^=lans;
		if(ch[0]=='Q') {
			scanf("%d",&k);k^=lans;
			int c=LCA(x,y);
			Query(rt[x],rt[y],rt[c],rt[f[c][0]],1,m,k);
			printf("%d\n",lans); 
		}
		else {
			add_edge(x,y);
			int u=g_fa(x),v=g_fa(y);
			if(sz[u]>sz[v]){swap(x,y);swap(u,v);}
			fa[u]=v;sz[v]+=sz[u];
			dfs(x,y);
		}
	}
	return 0;
}

标签:rs,int,合并,mid,ecnt,fa,SDOI2013,ls,启发式
来源: https://www.cnblogs.com/bestime/p/16473340.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有