题意:给出一片森林,每个点有点权,要求资瓷两个操作:询问两点间路径的第k小点权;加一条边
分析:如果没有合并操作的话就是裸的可持久化线段树啦。
但既然有合并操作那么我们就每次把两个块的可持久化线段树进行启发式合并。
何为启发式合并呢,其实就是暴力合并,把小一点的那棵树上的主席树全部进行重建,看上去很暴力,可据说可以证明复杂度均摊logn,反正我是不会证了……
这题调死宝宝了~~
调了大半天,最后才发现是一开始加边的时候忘了顺带维护并查集,导致万年RE……
代码:
#include
#include
#include
#include
#include
#define N 200005
using namespace std;
int n,m,q,tot,cnt,last[N],dep[N],fa[N][20],size[N],f[N],root[N],val[N];
struct tree{int l,r,s;}t[N*100];
struct edge{int to,next;}e[N*2];
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void updata(int d)
{
t[d].s=t[t[d].l].s+t[t[d].r].s;
}
void insert(int &d,int p,int l,int r,int x)
{
d=++tot;
t[d].s=t[p].s;
if (l==r)
{
t[d].s++;
return;
}
t[d].l=t[p].l;
t[d].r=t[p].r;
int mid=(l+r)/2;
if (x<=mid) insert(t[d].l,t[p].l,l,mid,x);
else insert(t[d].r,t[p].r,mid+1,r,x);
updata(d);
}
void dfs(int x)
{
for (int i=1;i<=16;i++)
fa[x][i]=0;
for (int i=1;i<=16;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
insert(root[x],root[fa[x][0]],0,1000000000,val[x]);
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==fa[x][0]) continue;
dep[e[i].to]=dep[x]+1;
fa[e[i].to][0]=x;
dfs(e[i].to);
}
}
int getlca(int x,int y)
{
if (dep[x]=0;i--)
if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if (x==y) return x;
for (int i=16;i>=0;i--)
if (fa[x][i]!=fa[y][i])
{
x=fa[x][i];y=fa[y][i];
}
return fa[x][0];
}
int getf(int x)
{
if (f[x]==x) return x;
f[x]=getf(f[x]);
return f[x];
}
int solveQ(int r1,int r2,int r3,int r4,int l,int r,int k)
{
if (l==r) return l;
int mid=(l+r)/2;
int s=t[t[r1].l].s+t[t[r2].l].s-t[t[r3].l].s-t[t[r4].l].s;
if (s>=k) return solveQ(t[r1].l,t[r2].l,t[r3].l,t[r4].l,l,mid,k);
else return solveQ(t[r1].r,t[r2].r,t[r3].r,t[r4].r,mid+1,r,k-s);
}
int query(int x,int y,int z)
{
int lca=getlca(x,y);
return solveQ(root[x],root[y],root[lca],root[fa[lca][0]],0,1000000000,z);
}
void link(int x,int y)
{
addedge(x,y);
int rx=getf(x),ry=getf(y);
if (size[rx]