森林

森林

题目描述:贴个传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3123

题解

用主席树维护区间k小。对于每个节点,以父亲节点为上一版本。
对于每次加边,进行启发式合并,暴力搜索子树内每几个节点,重构主席树即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#define N 80005
#define P 17
using namespace std;
int cyc,n,m,Q,cnt,pre,s[N],Tnum,num[N];
int w[N][P+1],rt[N],flag[N],dep[N];
int k,la[N],ff[N*2],fa[N],size[N];
struct node{int a,b;}map[N*2];
struct point{int lc,rc,size;}t[N*300];
int get(int x){return x^pre;}
void add(int a,int b)
{
  map[++k]=(node){a,b};ff[k]=la[a];la[a]=k;
  map[++k]=(node){b,a};ff[k]=la[b];la[b]=k;
}

class func_seg_tree
{
  public:
  void modify(int &x,int pre,int l,int r,int des)
  {
    t[x=++cnt]=t[pre];t[x].size++;
    if(l==r)return;
    int mid=l+r>>1;
    if(des<=mid)modify(t[x].lc,t[pre].lc,l,mid,des);
    else modify(t[x].rc,t[pre].rc,mid+1,r,des);
  }
  int qry(int a,int b,int c,int d,int l,int r,int k)
  {
    if(l==r)return num[l];
    int A=t[t[a].lc].size,B=t[t[b].lc].size;
    int C=t[t[c].lc].size,D=t[t[d].lc].size;
    int sum=A+B-C-D,mid=l+r>>1;
    if(k<=sum)return qry(t[a].lc,t[b].lc,t[c].lc,t[d].lc,l,mid,k);
    return qry(t[a].rc,t[b].rc,t[c].rc,t[d].rc,mid+1,r,k-sum);
  }  
}T;

void dfs(int x)
{
  flag[x]=1;size[x]=1;
  T.modify(rt[x],rt[w[x][0]],1,Tnum,s[x]);
  for(int i=1;i<=P;i++)
    w[x][i]=w[w[x][i-1]][i-1];
  for(int a=la[x];a;a=ff[a])
    if(!flag[map[a].b])
    {
      w[map[a].b][0]=x;dep[map[a].b]=dep[x]+1;
      dfs(map[a].b);size[x]+=size[map[a].b];
    }
  flag[x]=0;
}

int lca(int x,int y)
{
  if(dep[x]<dep[y])swap(x,y);
  for(int i=P;i>=0;i--)
    if(dep[w[x][i]]>=dep[y])x=w[x][i];
  if(x==y)return x;
  for(int i=P;i>=0;i--)
    if(w[x][i]!=w[y][i])x=w[x][i],y=w[y][i];
  return w[x][0];
}

int find(int x)
{
  if(!fa[x])return x;
  return fa[x]=find(fa[x]);
}

int main()
{
  int a,b,x,y,z,pos,X,Y;char ch;
  scanf("%d%d%d%d",&cyc,&n,&m,&Q);
  for(int i=1;i<=n;i++)
    scanf("%d",&s[i]),num[i]=s[i];
  sort(num+1,num+n+1);
  Tnum=unique(num+1,num+n+1)-num-1;
  for(int i=1;i<=n;i++)
    s[i]=lower_bound(num+1,num+Tnum+1,s[i])-num;
  for(int i=1;i<=m;i++)
    scanf("%d%d",&a,&b),add(a,b);
  for(int i=1;i<=n;i++)if(!w[i][0])dep[i]=1,dfs(i);
  while(Q--)
  {
    scanf(" %c%d%d",&ch,&x,&y);
    x=get(x);y=get(y);
    if(ch=='Q')
    {
      scanf("%d",&z);z=get(z);pos=lca(x,y);
      printf("%d\n",pre=T.qry(rt[x],rt[y],rt[pos],rt[w[pos][0]],1,Tnum,z));
    }
    else
    {
      int X=find(x),Y=find(y);
      if(size[X]>size[Y])swap(x,y);
      w[x][0]=y;dep[x]=dep[y]+1;
      dfs(x);add(x,y);
    }
  }
  return 0;
}

你可能感兴趣的:(森林)