zoukankan      html  css  js  c++  java
  • 【xsy2818】 最近点 动态树分治+可持久化线段树

    题目大意:给你一颗n个节点的树,最初点集S为空。

    有m次操作:往当前点集S中加入/删除一个点,询问点x至集合S中任意点的最小距离,回到第t次修改点集的操作后的状态。

    数据范围:$n,m≤10^5$

    我们先无视这个可持久化的要求,考虑下不可持久化怎么做。

    显然考虑动态树分治。

    令点v为当前分治中心,u为v在点分树上的父亲,

    每个点开一个数组D,D[x]表示以v为根的点分树中,与v距离为不大于x的点的标记点数量。

    我们借助这个数组,可以方便地求出从v走最少多少步可以走到一个标记点。

    首先考虑查询操作,我们可以直接在点分树上从x开始往上跳,设当前跳到了点y,我们需要在这个点的D数组中找到一个最大的k,满足D[k]=0。 那么我们不难发现此时找到的距离点x最近的点距离为$k+dis(x,y)+1$。

    至于为什么不需要像正常动态点分治那样容斥相减,那是因为此处我们只需要找到一个距离最近的即可,不去重也不会对答案有影响(这是我后来才发现的,场上写了相减的。。。。)

     

    至于修改操作,我们直接在点分树上从x开始网上跳,设当前跳到了y,我们修改下$D[dis(x,y)]$后面的数据即可。

    我们为了优化复杂度显然不可以暴力修改/查询D数组,在这里我们用线段树维护D数组即可。

    然而此题中还要求要可持久化,把线段树换成可持久化线段树就可以了。

    时间复杂度:$O(nlog^2 n)$

    代码后来优化了一波,不算太长。

      1 #include<bits/stdc++.h>
      2 #define M 100005
      3 #define N 20000005
      4 using namespace std;
      5 
      6 struct edge{int u,next;}e[M*2]={0}; int head[M]={0},Use=0;
      7 void add(int x,int y){Use++;e[Use].u=y;e[Use].next=head[x];head[x]=Use;}
      8 int T=0,newT=0,n,pointcnt[M]={0};
      9 
     10 int f[M][20]={0},dep[M]={0};
     11 void dfs(int x,int fa){
     12     f[x][0]=fa; dep[x]=dep[fa]+1;
     13     for(int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
     14     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa) dfs(e[i].u,x);
     15 }
     16 int getlca(int x,int y){
     17     if(dep[x]<dep[y]) swap(x,y); int cha=dep[x]-dep[y];
     18     for(int i=19;~i;i--) if((1<<i)&cha) x=f[x][i];
     19     for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
     20     if(x==y) return x; return f[x][0];
     21 }
     22 int getdis(int x,int y){return dep[x]+dep[y]-2*dep[getlca(x,y)];}
     23 
     24 int fa[M]={0},vis[M]={0},siz[M]={0},minn=0,minid=0;
     25 void dfssiz(int x,int fa){
     26     siz[x]=1;
     27     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa&&vis[e[i].u]==0) dfssiz(e[i].u,x),siz[x]+=siz[e[i].u];
     28 }
     29 void dfsmax(int x,int fa,int fsiz){
     30     int maxn=fsiz-siz[x];
     31     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa&&vis[e[i].u]==0) dfsmax(e[i].u,x,fsiz),maxn=max(maxn,siz[e[i].u]);
     32     if(maxn<minn) minn=maxn,minid=x;
     33 }
     34 int makeroot(int x){dfssiz(x,0); minn=M; dfsmax(x,0,siz[x]); return minid;}
     35 void solve(int x,int Fa){
     36     x=makeroot(x); vis[x]=1; fa[x]=Fa;
     37     for(int i=head[x];i;i=e[i].next) if(vis[e[i].u]==0) solve(e[i].u,x);
     38 }
     39 
     40 int lc[N]={0},rc[N]={0},sum[N]={0},use=0;
     41 void updata(int &x,int l,int r,int k,int val){
     42     use++; lc[use]=lc[x]; rc[use]=rc[x]; sum[use]=sum[x]+val;
     43     x=use; int mid=(l+r)>>1; if(l==r) return;
     44     if(k<=mid) updata(lc[x],l,mid,k,val); else updata(rc[x],mid+1,r,k,val);
     45 }
     46 int query(int x,int l,int r){
     47     if(sum[x]==0) return r;
     48     if(l==r) return -1; int mid=(l+r)>>1;
     49     int res=query(lc[x],l,mid);
     50     if(res!=mid) return res;
     51     int res2=query(rc[x],mid+1,r);
     52     if(res2!=-1) return res2; return res;
     53 }
     54 
     55 struct hh{
     56     int rt; hh(int RT=0){rt=RT;}
     57     int query(int x,int l,int r,int k){
     58         if(l==r) return sum[x]; int mid=(l+r)>>1;
     59         if(k<=mid) return query(lc[x],l,mid,k);
     60         return query(rc[x],mid+1,r,k);
     61     }
     62     int set(int &x,int l,int r,int k,int val){
     63         use++; lc[use]=lc[x]; rc[use]=rc[x]; x=use; 
     64         if(l==r) return sum[x]=val;
     65         int mid=(l+r)>>1;
     66         if(k<=mid) set(lc[x],l,mid,k,val);
     67         else set(rc[x],mid+1,r,k,val);
     68     }
     69     int Query(int id){return query(rt,1,n,id);}
     70     int Set(int id,int val){return set(rt,1,n,id,val);}
     71 }a[M],on[M];
     72 
     73 void Updata(int x,int id,int val){
     74     int rt=a[newT].Query(x);
     75     updata(rt,0,n,getdis(x,id),val);
     76     a[newT].Set(x,rt);
     77     if(fa[x]) Updata(fa[x],id,val);
     78 }
     79 int Query(int x,int y){
     80     int res=query(a[T].Query(x),0,n)+getdis(x,y);
     81     if(fa[x]) res=min(res,Query(fa[x],y));
     82     return res;
     83 }
     84 
     85 int main(){
     86     scanf("%d",&n);
     87     for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
     88     dfs(1,0);
     89     solve(1,0);
     90     int m,ans=0; scanf("%d",&m);
     91     while(m--){
     92         int op,x; scanf("%d%d",&op,&x); x^=ans;
     93         if(op==3){T=x; continue;}
     94         if(op==1){
     95             newT++; a[newT]=a[T]; on[newT]=on[T];
     96             int sel=on[T].Query(x);
     97             if(sel==0){
     98                 pointcnt[newT]=pointcnt[T]+1;
     99                 on[newT].Set(x,1);
    100             }else{
    101                 pointcnt[newT]=pointcnt[T]-1;
    102                 on[newT].Set(x,0);
    103             }
    104             Updata(x,x,sel==0?1:-1);
    105             T=newT;
    106         }else{
    107             if(pointcnt[T]==0) {printf("%d
    ",ans=1e9); continue;}
    108             printf("%d
    ",ans=Query(x,x)+1);
    109         }
    110     }
    111 }
  • 相关阅读:
    【转】关于LWF——线性工作流
    【转】对抗拖库 ―― Web 前端慢加密
    【转】用C#调用Windows API向指定窗口发送
    使用 Redis 如何设计分布式锁?
    SpringBoot如何使用WebSocket实现前后端交互?
    Redis缓存使用中的热key问题
    Spring的BeanUtils的copyProperties方法需要注意的点
    解决github中图片不显示的问题
    java中JsonSerializer的用法(前后端单位转换必备)
    Spring Boot2.X中findOne的用法
  • 原文地址:https://www.cnblogs.com/xiefengze1/p/10556396.html
Copyright © 2011-2022 走看看