• 虚树(树形dp套路)模板——bzoj2286


    虚树的核心就是把关键点和关键点的lca重新生成一棵树,然后在这棵树上进行dp

    https://www.cnblogs.com/zwfymqz/p/9175152.html  写的很好的博客

    建立虚树的核心代码

    void insert(int x){
        if(top==1){stk[++top]=x;return;}
        int lca=LCA(x,stk[top]);
        if(lca==stk[top])return;//这里本来要stk[++top]=x的,但是由于本题特殊性,所以删去优化时间
        while(top>1 && dfn[lca]<=dfn[stk[top-1]])
            add_edge(stk[top-1],stk[top]),--top;
        if(lca!=stk[top])//如果lca不是关键点,把它做成关键点,即把lca和栈顶元素连边,然后栈顶元素出栈,lca进栈
            add_edge(lca,stk[top]),stk[top]=lca;
        stk[++top]=x;
    } 

    本题的ac代码

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 250005
    #define ll long long 
    struct Edge{int to,nxt,w;}e[maxn<<1];
    int head[maxn],tot;
    void add(int u,int v,int w){
        e[tot].nxt=head[u];e[tot].to=v;e[tot].w=w;head[u]=tot++;
    }
    //树链剖分
    ll Min[maxn];
    int fa[maxn],son[maxn],topf[maxn],size[maxn],d[maxn],dfn[maxn],ind;
    void dfs1(int u,int pre){
        fa[u]=pre;size[u]=1;
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].to;
            if(v==pre)continue;
            d[v]=d[u]+1;
            Min[v]=min(Min[u],(ll)e[i].w);
            dfs1(v,u);
            size[u]+=size[v];
            if(size[v]>size[son[u]])son[u]=v;
        }
    }
    void dfs2(int u,int tp){
        topf[u]=tp;dfn[u]=++ind;
        if(!son[u])return;
        dfs2(son[u],tp);
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].to;
            if(!topf[v])
                dfs2(v,v);
        }
    }
    //求lca
    int LCA(int x,int y){
        while(topf[x]!=topf[y]){
            if(d[topf[x]]<d[topf[y]])
                swap(x,y);
            x=fa[topf[x]];
        }
        if(d[x]>d[y])swap(x,y);
        return x;
    }
    //建立虚树 
    
    int top,stk[maxn];
    vector<int>G[maxn];
    void add_edge(int u,int v){G[u].push_back(v);}
    
    void insert(int x){
        if(top==1){stk[++top]=x;return;}
        int lca=LCA(x,stk[top]);
        if(lca==stk[top])return;
        while(top>1 && dfn[lca]<=dfn[stk[top-1]])//把lca以下的关键点都入栈 
            add_edge(stk[top-1],stk[top]),--top;
        if(lca!=stk[top])//如果lca不是关键点,把它做成关键点 
            add_edge(lca,stk[top]),stk[top]=lca;
        stk[++top]=x;
    } 
    //在虚树上dp
    ll dfs3(int u){
        if(G[u].size()==0)return Min[u];
        ll res=0;
        for(int i=0;i<G[u].size();i++)
            res+=dfs3(G[u][i]);
        G[u].clear();
        return min(res,Min[u]);
    }
    
    int cmp(int a,int b){return dfn[a]<dfn[b];}
    
    int n,m,x,y,z,a[maxn];
    
    int main(){
        memset(head,-1,sizeof head);
        Min[1]=1ll<<60;
        cin>>n;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);add(y,x,z);
        }
        d[1]=1;
        dfs1(1,0),dfs2(1,1);
        
        cin>>m;
        while(m--){
            int k;scanf("%d",&k);
            for(int i=1;i<=k;i++)scanf("%d",&a[i]);
            sort(a+1,a+1+k,cmp);
            top=1;
            stk[top]=1;G[1].clear();
            
            for(int i=1;i<=k;i++)insert(a[i]);
            while(top)
                add_edge(stk[top-1],stk[top]),top--;
            
            //进行dp 
            cout<<dfs3(1)<<endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    P3803 【模板】多项式乘法(FFT)
    P2264 情书 Trie匹配
    CF877E Danil and a Part-time Job 线段树维护dfs序
    P3810 【模板】三维偏序(陌上花开)
    LOJ #6282. 数列分块入门 6
    LOJ #6281. 数列分块入门 5
    LOJ #6280. 数列分块入门 4
    LOJ #6279. 数列分块入门 3
    LOJ #6278. 数列分块入门 2
    LOJ #6277. 数列分块入门 1
  • 原文地址:https://www.cnblogs.com/zsben991126/p/11192377.html
Copyright © 2020-2023  润新知