• [模板] 虚树


    用途

    树上多次询问,每次询问关于一些关键点,关键点总个数给定限制;单拿出来每个询问,基本上树形dp可以解决

    思路

    把每次询问的点和询问的点的lca(即关键点)浓缩到虚树上,两点之间的连边包含原树中两点间路径的信息,再在虚树上暴力(?)处理

    做法

    先yy一个在原树上对于单一询问的做法,然后把它放到虚树上来做

    那么怎么建虚树呢

    先按照dfs序排序,这样可以保证做到不在x的子树中的点时,x的子树中的点都已经做完了

    用一个栈来记录从虚树根到当前做到的点的链。虽然树根是谁都行,但方便起见直接给成1,并把1和第一个点压入栈中

    现在新加入一个点p,设栈顶元素是x,p和x的最近公共祖先是lca

    有两种情况(lca绝对不会等于p,因为是按dfs序做的):

    1.lca=x,直接把p压入栈中

    2.lca!=x

      这说明lca在x的上面,而且x的子树已经做完了,我们要在栈中找到一个合适的位置把lca放下来,再把x连到一个合适的点上,再把x踢掉,再把p加进去

      设栈顶元素是y,再记一个last表示刚才踢掉的元素

      当dfn[y]>dfn[lca]时,循环地,把last连到y上(如果有的话),然后把y也踢掉

      当跳出循环时,有可能lca=y,如果不等的话,就把lca压到栈中作为栈顶

      再把刚才还没连的那个last连到lca上,最后把p也压到栈里。

      注意连边的时候记录路径信息

    这样在建出的虚树点数是$O(k)$的(k是询问点数),在他上面上乱搞就可以了。

    注意由于询问数很多,万万不可memset,为了方便清空,可以记下来树中的每个点,然后依次清零

    例题

    luogu2495 消耗战

    建出虚树后是一个比较显然的treedp(写的很丑所以只看build好了)

      1 #include<bits/stdc++.h>
      2 #define pa pair<int,int>
      3 #define CLR(a,x) memset(a,x,sizeof(a))
      4 using namespace std;
      5 typedef long long ll;
      6 const int maxn=25e4+10,inf=1e9;
      7 
      8 inline ll rd(){
      9     ll x=0;char c=getchar();int neg=1;
     10     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
     11     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
     12     return x*neg;
     13 }
     14 
     15 int eg[maxn*2][3],egh[maxn],ect;
     16 int N,M,K;
     17 int dep[maxn],fa[maxn][20],dfn[maxn],md[maxn][20],tot;
     18 int ky[maxn],stk[maxn],sh;
     19 int son[maxn],bro[maxn][2];
     20 ll f[maxn];
     21 bool flag[maxn];
     22 
     23 inline void adeg(int a,int b,int c){
     24     eg[++ect][0]=b,eg[ect][2]=c;eg[ect][1]=egh[a];egh[a]=ect;
     25 }
     26 
     27 void dfs(int x){
     28     for(int i=0;fa[x][i]&&fa[fa[x][i]][i];i++){
     29         fa[x][i+1]=fa[fa[x][i]][i];
     30         md[x][i+1]=min(md[x][i],md[fa[x][i]][i]);
     31     }
     32     dfn[x]=++tot;
     33     for(int i=egh[x];i;i=eg[i][1]){
     34         int b=eg[i][0];
     35         if(b==fa[x][0]) continue;
     36         md[b][0]=eg[i][2];
     37         fa[b][0]=x,dep[b]=dep[x]+1;
     38         dfs(b);
     39     }
     40 }
     41 
     42 pa getlca(int x,int y){
     43     int mi=inf;
     44     if(dep[x]<dep[y]) swap(x,y);
     45     for(int i=log2(dep[x]-dep[y]);i>=0&&dep[x]!=dep[y];i--){
     46         if(dep[fa[x][i]]>=dep[y])
     47             mi=min(mi,md[x][i]),x=fa[x][i];
     48     }
     49     if(x==y) return make_pair(x,mi);
     50     for(int i=log2(dep[x]);i>=0;i--){
     51         if(fa[x][i]!=fa[y][i])
     52             mi=min(mi,min(md[x][i],md[y][i])),x=fa[x][i],y=fa[y][i];
     53     }
     54     return make_pair(fa[x][0],min(mi,min(md[x][0],md[y][0])));
     55 }
     56 
     57 inline bool cmp(int x,int y){return dfn[x]<dfn[y];}
     58 
     59 inline void adson(int x,int y){
     60     bro[y][0]=son[x],son[x]=y,bro[y][1]=getlca(x,y).second;
     61 }
     62 
     63 inline void build(){
     64     stk[sh=1]=1;
     65     for(int i=1;i<=K;i++){
     66         int lca=getlca(ky[i],stk[sh]).first;
     67         int lst=0;
     68         while(dfn[lca]<dfn[stk[sh]]){
     69             if(lst) adson(stk[sh],lst);
     70             lst=stk[sh--];
     71         }
     72         if(lca!=stk[sh]) stk[++sh]=lca;
     73         if(lst) adson(stk[sh],lst);
     74         if(ky[i]!=1) stk[++sh]=ky[i];
     75     }
     76     while(sh>1) adson(stk[sh-1],stk[sh]),sh--;
     77 }
     78 
     79 void dp(int x){
     80     f[x]=0;
     81     for(int i=son[x];i;i=bro[i][0]){
     82         dp(i);
     83         if(!flag[i]) f[x]+=min(f[i],1ll*bro[i][1]);
     84         else f[x]+=bro[i][1];
     85     }
     86     son[x]=0;
     87 }
     88 
     89 int main(){
     90     //freopen("","r",stdin);
     91     int i,j,k;
     92     N=rd();
     93     for(i=1;i<N;i++){
     94         int a=rd(),b=rd(),c=rd();
     95         adeg(a,b,c);adeg(b,a,c);
     96     }
     97     dep[1]=1;dfs(1);
     98     M=rd();
     99     for(i=1;i<=M;i++){
    100         K=rd();
    101         for(j=1;j<=K;j++)
    102             ky[j]=rd(),flag[ky[j]]=1;
    103         sort(ky+1,ky+K+1,cmp);
    104         build();
    105         dp(1);
    106         printf("%lld
    ",f[1]);
    107         for(j=1;j<=K;j++)
    108             flag[ky[j]]=0;
    109     }
    110     return 0;
    111 }
    View Code
  • 相关阅读:
    50个C/C++经典面试题
    多继承的构造顺序
    sizeof(struct)
    c++ 实现strcpy(),strlen()
    十天冲刺-01
    学习进度条(第八周)
    梦断代码阅读笔记01
    学习进度条(第七周)
    团队作业记账本开发NABCD
    学习进度条(第六周)
  • 原文地址:https://www.cnblogs.com/Ressed/p/9811784.html
Copyright © 2020-2023  润新知