森林 bzoj-3123 Sdoi-2013

题目大意:给定一片共n个点的森林,T个操作,支持:连接两个不在一棵树上的两个点;查询一棵树上路径k小值。

注释:$1\le n,T \le 8\cdot 10^4$

想法:运用冯老师讲的方法:

“对于一个开起来非常困难的问题,我们可以通过先构造拟对象,然后向完全对象转化”

这个题,我们看到了最后一个操作,想到了主席树。

两个点x、y之间的路径,我们在主席树上用root[x]+root[y]-root[lca(x,y)]-root[fa[lca(x,y)]]的权值线段树即可。

那么,如果加上连接操作呢?

直接启发式合并,然后重置一部分的主席树即可。(重置的时候记得把倍增的数组也重置)

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
int cnt,tot,ans,len,a,b,tcase,n,m,q;
char s[5];
int x,y,z,d[80010],h[80010],v[80010];
map<int,int>g;
int to[200010],vis[80010],anc[80010],size[80010],ls[20000010],rs[20000010],nxt[200010],head[200010],f[80010][26],sum[20000010],root[2000010];
inline void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
int lca(int x,int y)
{
if(d[x]<d[y])
{
swap(x,y);
}
int dep=d[x]-d[y];
for(int i=0;i<=23;i++)
{
if((dep&(1<<i))!=0)
{
x=f[x][i];
}
}
if(x==y)
{
return x;
}
for(int i=23;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int updata(int pre,int l,int r,int v)
{
int pos=++cnt,mid=(l+r)>>1;
ls[pos]=ls[pre];
rs[pos]=rs[pre];
sum[pos]=sum[pre]+1;
if(l==r)
{
return pos;
}
else
{
if(v<=mid)
{
ls[pos]=updata(ls[pre],l,mid,v);
}
else
{
rs[pos]=updata(rs[pre],mid+1,r,v);
}
}
return pos;
}
int query(int x,int y,int fa,int anc,int l,int r,int k)
{
int mid=(l+r)>>1;
if(l==r)
{
return g[l];
}
int num=sum[ls[x]]+sum[ls[y]]-sum[ls[fa]]-sum[ls[anc]];
if(k<=num)
{
return query(ls[x],ls[y],ls[fa],ls[anc],l,mid,k);
}
else
{
return query(rs[x],rs[y],rs[fa],rs[anc],mid+1,r,k-num);
}
}
void dfs(int x,int fa,int ac)
{
vis[x]=1;
anc[x]=ac;
f[x][0]=fa;
d[x]=d[fa]+1;
root[x]=updata(root[fa],1,n,v[x]);
for(int i=1;i<=23;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=head[x];i;i=nxt[i])
{
if(to[i]!=fa)
{
dfs(to[i],x,ac);
size[x]+=size[to[i]];
}
}
}
int main()
{
scanf("%d",&tcase);
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
h[i]=v[i];
size[i]=1;
}
sort(h+1,h+1+n);
len=unique(h+1,h+1+n)-h-1;
for(int i=1;i<=n;i++)
{
int val=v[i];
v[i]=lower_bound(h+1,h+1+len,v[i])-h;
g[v[i]]=val;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
dfs(i,0,i);
}
while(q--)
{
scanf("%s",s);
if(s[0]=='Q')
{
scanf("%d%d%d",&x,&y,&z);
x=x^ans;
y=y^ans;
z=z^ans;
a=lca(x,y);
b=f[a][0];
ans=query(root[x],root[y],root[a],root[b],1,n,z);
printf("%d\n",ans);
}
else
{
scanf("%d%d",&x,&y);
x=x^ans;
y=y^ans;
add(x,y);
add(y,x);
if(size[anc[x]]<size[anc[y]])
{
size[anc[y]]+=size[anc[x]];
dfs(x,y,anc[y]);
}
else
{
size[anc[x]]+=size[anc[y]];
dfs(y,x,anc[x]);
}
}
}
}

小结:主席树活用起来真tm吓人... ...

[bzoj3123][Sdoi2013]森林_主席树_启发式合并的更多相关文章

  1. 【BZOJ3123】森林(主席树,启发式合并)

    题意:一个带点权的森林,要求维护以下操作: 1.询问路径上的点权K大值 2.两点之间连边 n,m<=80000 思路:如果树的结构不发生变化只需要维护DFS序 现在因为树的结构发生变化,要将两棵 ...

  2. [bzoj3123][sdoi2013森林] (树上主席树+lca+并查集启发式合并+暴力重构森林)

    Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...

  3. bzoj3123 [Sdoi2013]森林 树上主席树+启发式合并

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=3123 题解 如果是静态的查询操作,那么就是直接树上主席树的板子. 但是我们现在有了一个连接两棵 ...

  4. bzoj 3123 [Sdoi2013]森林(主席树+启发式合并+LCA)

    Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...

  5. BZOJ3123[Sdoi2013]森林——主席树+LCA+启发式合并

    题目描述 输入 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负 ...

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

    P3302 [SDOI2013]森林 主席树+启发式合并 (我以前的主席树板子是错的.......坑了我老久TAT) 第k小问题显然是主席树. 我们对每个点维护一棵包含其子树所有节点的主席树 询问(x ...

  7. bzoj 3123 [Sdoi2013]森林(主席树,lca,启发式合并)

    Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...

  8. BZOJ2123 [Sdoi2013]森林 【主席树 + 启发式合并】

    题目 输入格式 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负 ...

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

    传送门 显然树上第k大直接主席树 如果连边的话,我们重构小的那一棵,连到另一棵上. 说起来简单,调了我一晚上. 总的来说3个错误: 1.离散化写错位置写到了后面 2."="写成了& ...

随机推荐

  1. JS容易犯错的this和作用域

    var someuser = { name: 'byvoid', func: function() { console.log(this.name); } }; var foo = { name: ' ...

  2. POJ 2728(最优比率生成树+01规划)

                                                                                                    Dese ...

  3. openStack 主机流量计运行状态 随笔记录

    root@ruiy-controller:~# ifconfigeth0      Link encap:Ethernet  HWaddr 0c:c4:7a:0d:97:2c          ine ...

  4. JS页面刷新保持数据不丢失

    转自:https://blog.csdn.net/qq_32439101/article/details/78134877 下面是 DOM Storage 的接口定义: interface Stora ...

  5. E20170915-hm

    client n. 顾客; 当事人; 诉讼委托人; [计算机] 客户端; seal  n. 密封; 印章; 海豹; 封条;  v. 密封; 盖章; 决定; 封上(信封); sheet  n. 纸; 被 ...

  6. hdu5249

    这个是去年astar的题~ 标准做法主席树,然而渣渣并不会(我确实叫zhazha...), 所以,他先离线,离散化,然后树状数组+二分水过了.... 离线的目的主要是为了离散化,剩下的就和用一个树状数 ...

  7. 使用IDEA 搭建一个SpringBoot + Hibernate + Gradle

    ---恢复内容开始--- 打开IDEA创建一个新项目: 第一步: 第二步: 第三步: 最后一步: 如果下载的时候时间太久.可以找到build.gradle文件,添加以下代码.如下图 maven{ ur ...

  8. 利用MediaSession发送信息到蓝牙音箱

    1.利用MediaSession发送信息到蓝牙音箱,如:播放音乐时接收的歌曲信息,但是每一首歌连续播放时,再次发送的重复信息会被丢弃.则利用MediaSession发现信息时,要保证信息的不重复性. ...

  9. SQL中EXTRACT() 函数

    EXTRACT()("提取"的意思) 函数用于返回日期/时间的单独部分,比如年.月.日.小时.分钟等等. 就是返回出来具体的年,月,日 2008-12-29 16:25:46.63 ...

  10. ajax 三级联动写法

    主页面代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...