您的位置:首页 > 其它

[bzoj3123][Sdoi2013]森林_主席树_启发式合并

2018-07-24 21:31 513 查看

森林 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吓人... ...

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: