zoukankan      html  css  js  c++  java
  • [spojQTREE5]Query on a tree V

    合理的正解大概是动态点分治,这里给出其实现

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 100005
      4 struct Edge{
      5     int nex,to;
      6 }edge[N<<1];
      7 multiset<int>S[N],mnS[N];
      8 int n,m,E,x,y,head[N],fa[N],vis[N];
      9 void add(int x,int y){
     10     edge[E].nex=head[x];
     11     edge[E].to=y;
     12     head[x]=E++;
     13 }
     14 namespace DIST{
     15     int dep[N],f[N][20];
     16     void dfs(int k,int fa,int s){
     17         f[k][0]=fa,dep[k]=s;
     18         for(int i=1;i<20;i++)f[k][i]=f[f[k][i-1]][i-1];
     19         for(int i=head[k];i!=-1;i=edge[i].nex)
     20             if (edge[i].to!=fa)dfs(edge[i].to,k,s+1);
     21     }
     22     int lca(int x,int y){
     23         if (dep[x]<dep[y])swap(x,y);
     24         for(int i=19;i>=0;i--)
     25             if (dep[f[x][i]]>=dep[y])x=f[x][i];
     26         if (x==y)return x;
     27         for(int i=19;i>=0;i--)
     28             if (f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
     29         return f[x][0];
     30     }
     31     int dis(int x,int y){
     32         return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
     33     }
     34 };
     35 namespace DIVIDE{
     36     int rt,sz[N],vis[N];
     37     void get_sz(int k,int fa){
     38         sz[k]=1;
     39         for(int i=head[k];i!=-1;i=edge[i].nex)
     40             if ((!vis[edge[i].to])&&(edge[i].to!=fa)){
     41                 get_sz(edge[i].to,k);
     42                 sz[k]+=sz[edge[i].to];
     43             }
     44     }
     45     void get_rt(int k,int fa,int s){
     46         int mx=s-sz[k];
     47         for(int i=head[k];i!=-1;i=edge[i].nex)
     48             if ((!vis[edge[i].to])&&(edge[i].to!=fa)){
     49                 get_rt(edge[i].to,k,s);
     50                 mx=max(mx,sz[edge[i].to]);
     51             }
     52         if (mx<=(s>>1))rt=k;
     53     }
     54     int dfs(int k){
     55         get_sz(k,0);
     56         get_rt(k,0,sz[k]);
     57         k=rt,vis[k]=1;
     58         for(int i=head[k];i!=-1;i=edge[i].nex)
     59             if (!vis[edge[i].to])fa[dfs(edge[i].to)]=k;
     60         return k;
     61     }
     62 };
     63 void add(int k){
     64     if (!S[k].empty())mnS[fa[k]].insert(*S[k].begin());
     65 }
     66 void del(int k){
     67     if (!S[k].empty())mnS[fa[k]].erase(mnS[fa[k]].find(*S[k].begin()));
     68 }
     69 void Add(int k){
     70     mnS[k].insert(0);
     71     for(int i=k;fa[i];i=fa[i]){
     72         del(i);
     73         S[i].insert(DIST::dis(k,fa[i]));
     74         add(i);
     75     }
     76 }
     77 void Del(int k){
     78     mnS[k].erase(mnS[k].find(0));
     79     for(int i=k;fa[i];i=fa[i]){
     80         del(i);
     81         S[i].erase(S[i].find(DIST::dis(k,fa[i])));
     82         add(i);
     83     }
     84 }
     85 void update(int k){
     86     if (vis[k])Del(k);
     87     vis[k]^=1;
     88     if (vis[k])Add(k);
     89 }
     90 int query(int k){
     91     int ans=0x3f3f3f3f;
     92     if (mnS[k].size())ans=(*mnS[k].begin());
     93     for(int i=k;fa[i];i=fa[i]){
     94         del(i);
     95         if (!mnS[fa[i]].empty())ans=min(ans,(*mnS[fa[i]].begin())+DIST::dis(k,fa[i]));
     96         add(i);
     97     }
     98     if (ans==0x3f3f3f3f)ans=-1;
     99     return ans;
    100 }
    101 int main(){
    102     scanf("%d",&n);
    103     memset(head,-1,sizeof(head));
    104     for(int i=1;i<n;i++){
    105         scanf("%d%d",&x,&y);
    106         add(x,y),add(y,x);
    107     }
    108     DIST::dfs(1,0,0);
    109     DIVIDE::dfs(1);
    110     scanf("%d",&m);
    111     for(int i=1;i<=m;i++){
    112         scanf("%d%d",&x,&y);
    113         if (!x)update(y);
    114         else printf("%d
    ",query(y));
    115     }
    116     return 0;
    117 }
    View Code

    下面,来考虑LCT的做法:

    与求最小生成树的LCT相同,将边拆成点,并将边权变为点权

    为了方便,这里给出一些定义:

    1.称一个点对应的实链为Splay上其子树内所有点(显然这些点构成一段实链)

    2.称一个点的子树范围为自身$cup $其Splay上左右儿子的子树范围$cup $所有虚儿子的子数范围

    (也可以理解为原树中对应的实链上所有节点虚儿子子树的并)

    对每一个节点,维护以下信息:

    1.对应的实链点权和(即Splay上子树点权和)

    2.其子树范围内到其对应的实链链顶/链尾距离最小的白点(的距离)

    3.其自身和每一个虚儿子的子树范围内到其距离最小的白点(的距离)所构成的集合(距离不包括其自身的权值,这是为了方便修改权值)

    通过这些信息,只需要将查询点make_root到根,那么此时整个Splay的根(并不是原树的根)的第2个信息即为答案(但由于可能找不到该位置,不妨再splay(x)一下)

    下面,问题即如何维护这些信息,实际上LCT的信息维护基本都只需要考虑以下两种变化:

    1.修改了Splay上子树的信息(即重新up),那么其中第1个信息容易维护,第3个信息没有影响,下面来考虑如何维护第2个信息(以链顶为例)——

    将其子树范围分为三类:

    (1)Splay上左儿子的子树范围,这个即为左儿子的该信息

    (2)Splay上右儿子的子树范围,考虑其对应的实链,即右儿子先走到自己对应的链链顶,再从其通过左儿子对应的整条实链即可,那么即右儿子的该信息+其点权+左儿子的第1个信息

    (3)自身$cup $虚儿子的子树范围,同样要先到达其,即第3个信息维护的集合中最小值+左儿子的第1个信息

    2.增加/删除了某个虚儿子(不维护子树信息的LCT对此无影响),此时即要求该虚儿子子树范围内到其距离最小的点(的距离),将虚儿子的第2个信息(链顶)+其点权在集合中加入或删除即可

    综上,时间复杂度为$o(nlog^{2}n)$,可以通过

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 200005
      4 multiset<int>S[N];
      5 int n,m,x,y,vis[N],st[N],fa[N],tag[N],val[N],sum[N],ch[N][2],f[N][2];
      6 bool check(int k){
      7     return (ch[fa[k]][0]!=k)&&(ch[fa[k]][1]!=k);
      8 }
      9 int which(int k){
     10     return ch[fa[k]][1]==k;
     11 }
     12 void rev(int k){
     13     tag[k]^=1;
     14     swap(ch[k][0],ch[k][1]);
     15     swap(f[k][0],f[k][1]);
     16 }
     17 void add_vir(int k){
     18     S[fa[k]].insert(f[k][0]);
     19 }
     20 void del_vir(int k){
     21     S[fa[k]].erase(S[fa[k]].find(f[k][0]));
     22 }
     23 int get_min(int k){
     24     if (S[k].empty())return 0x3f3f3f3f;
     25     return (*S[k].begin());
     26 }
     27 void up(int k){
     28     sum[k]=sum[ch[k][0]]+sum[ch[k][1]]+val[k];
     29     f[k][0]=min(f[ch[k][0]][0],min(get_min(k),f[ch[k][1]][0])+val[k]+sum[ch[k][0]]);
     30     f[k][1]=min(f[ch[k][1]][1],min(get_min(k),f[ch[k][0]][1])+val[k]+sum[ch[k][1]]);
     31 }
     32 void down(int k){
     33     if (tag[k]){
     34         if (ch[k][0])rev(ch[k][0]);
     35         if (ch[k][1])rev(ch[k][1]);
     36         tag[k]=0;
     37     }
     38 }
     39 void rotate(int k){
     40     int f=fa[k],g=fa[f],p=which(k);
     41     fa[k]=g;
     42     if (!check(f))ch[g][which(f)]=k;
     43     fa[ch[k][p^1]]=f,ch[f][p]=ch[k][p^1];
     44     fa[f]=k,ch[k][p^1]=f;
     45     up(f),up(k);
     46 }
     47 void splay(int k){
     48     for(int i=k;;i=fa[i]){
     49         st[++st[0]]=i;
     50         if (check(i))break;
     51     }
     52     while (st[0])down(st[st[0]--]);
     53     for(int i=fa[k];!check(k);i=fa[k]){
     54         if (!check(i)){
     55             if (which(i)==which(k))rotate(i);
     56             else rotate(k);
     57         }
     58         rotate(k);
     59     }
     60 }
     61 void access(int k){
     62     int lst=0;
     63     while (k){
     64         splay(k);
     65         if (ch[k][1])add_vir(ch[k][1]);
     66         if (lst)del_vir(lst);
     67         ch[k][1]=lst,up(k);
     68         lst=k,k=fa[k];
     69     }
     70 }
     71 void make_root(int k){
     72     access(k);
     73     splay(k);
     74     rev(k);
     75 }
     76 void add(int x,int y){
     77     make_root(x);
     78     make_root(y);
     79     fa[y]=x,add_vir(y),up(x);
     80 }
     81 void upd_val(int k,int x){
     82     make_root(k);
     83     val[k]=x,up(k);
     84 }
     85 void upd_col(int k){
     86     make_root(k);
     87     if (vis[k])S[k].erase(S[k].find(0));
     88     vis[k]^=1;
     89     if (vis[k])S[k].insert(0);
     90     up(k);
     91 }
     92 int query(int k){
     93     make_root(k);
     94     if (f[k][0]==0x3f3f3f3f)return -1;
     95     return f[k][0];
     96 }
     97 int main(){
     98     scanf("%d",&n);
     99     memset(f,0x3f,sizeof(f));
    100     for(int i=1;i<n;i++){
    101         scanf("%d%d",&x,&y);
    102         add(x,i+n),add(y,i+n);
    103         upd_val(i+n,1);
    104     }
    105     scanf("%d",&m);
    106     for(int i=1;i<=m;i++){
    107         scanf("%d%d",&x,&y);
    108         if (!x)upd_col(y);
    109         else printf("%d
    ",query(y));
    110     }
    111     return 0;
    112 }
    View Code
  • 相关阅读:
    如何判断轮廓是否为圆(算法更新)
    近期购置的CV&AI类图书梳理
    基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合
    大厂们的 redis 集群方案
    redis 突然大量逐出导致读写请求block
    Docker 1.13 管理命令
    玩转 Ceph 的正确姿势
    Docker 常用命令
    git常用命令
    从C++到GO
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/15330813.html
Copyright © 2011-2022 走看看