• [SDOI2011]消耗战


    O(n^2)的dp很显然

    以1为根

    f[x]表示把以为根的子树都砍断的最小代价

    f[x]=∑min(f[y],e[i].val)

    但是对于K=500000的

    发现,每次用到的关键点并不多,是所有关键点和dfn序相邻关键点的LCA,

    这启示我们用虚树!

    虚树的边权就是路径上链的最小值

    总点数是2*K的

    至于虚树怎么建?

    考虑树欧拉序

    虚树的欧拉序的相对顺序显然不变

    把所有的点的进栈出栈的两个dfn序都放在数组里排序

    然后用栈模拟建出树即可

    (当然,由于dfs本身就是栈的操作,所以用栈模拟dfs就不用建树了)

    O(K*logN)

    代码:

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define il inline
    #define reg register int
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=250000+5;
    const int inf=0x3f3f3f3f;
    int n,m;
    struct node{
        int nxt,to;
        int val;
    }e[2*N],b[2*N];
    int hd[N],cnt;
    int pre[N],tot;
    void add(int x,int y,int z){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        e[cnt].val=z;
        hd[x]=cnt;
    }
    int dfn[N],df;
    int dfn2[N];
    int fdfn[2*N];
    int fa[N][20],mi[N][20];
    int dep[N];
    void dfs(int x,int d){
        dep[x]=d;
        dfn[x]=++df;
        fdfn[df]=x;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa[x][0]) continue;
            fa[y][0]=x;
            mi[y][0]=e[i].val;
            dfs(y,d+1);
        }
        dfn2[x]=++df;
        fdfn[df]=-x;
    }
    int lca(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        //cout<<" x "<<x<<" : "<<dep[x]<<" "<<fa[x][0]<<endl;
        ///cout<<" y "<<y<<" : "<<dep[y]<<" "<<fa[y][0]<<endl;
        for(reg j=19;j>=0;--j){
            if(dep[fa[x][j]]>=dep[y]){
                x=fa[x][j];
            }
        }
        if(x==y) return x;
        for(reg j=19;j>=0;--j){
            if(fa[x][j]!=fa[y][j]){
                x=fa[x][j],y=fa[y][j];
            }
        }
        return fa[x][0];
    }
    int fin(int x,int anc){//x!=anc
        int ret=inf;
        for(reg j=19;j>=0;--j){
            if(fa[x][j]&&dep[fa[x][j]]>=dep[anc]){
                ret=min(ret,mi[x][j]);
                x=fa[x][j];
            }
        }
        //cout<<x<<" "<<anc<<" ret "<<ret<<endl;
        return ret;
    }
    int mem[2*N];
    int id[4*N],num;
    bool cmp(int x,int y){
        return dfn[x]<dfn[y];
    }
    int vis[N],has[N];
    int sta[4*N],top;
    ll f[N];
    void sol(int x,int fafa){
        f[x]=0;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fafa) continue;
            sol(y,x);
            f[x]+=min(f[y],(ll)e[i].val);
        }
        if(has[x]) f[x]=inf;
    }
    int main(){
        rd(n);
        int x,y,z;
        for(reg i=1;i<n;++i){
            rd(x);rd(y);rd(z);
            add(x,y,z);add(y,x,z);
        }
        memset(mi,inf,sizeof mi);
        dfs(1,1);
        
        for(reg j=1;j<=19;++j){
            for(reg i=1;i<=n;++i){
                fa[i][j]=fa[fa[i][j-1]][j-1];
                mi[i][j]=min(mi[i][j-1],mi[fa[i][j-1]][j-1]);
            }
        }
        
        int k;
        
        memset(hd,0,sizeof hd);
        cnt=0;
        
        rd(m);
        while(m--){
            rd(k);
            for(reg i=1;i<=k;++i) rd(mem[i]),has[mem[i]]=1;
            sort(mem+1,mem+k+1,cmp);
            top=0;
            tot=k;
            for(reg i=k-1;i>=1;--i){
                if(mem[i]==mem[i+1]) continue;
                int anc=lca(mem[i],mem[i+1]);
            //    cout<<mem[i]<<" and "<<mem[i+1]<<" anc "<<anc<<endl;
                mem[++tot]=anc;
            }
            mem[++tot]=1;
            
            num=0;
            for(reg i=1;i<=tot;++i){
            //    cout<<" i "<<mem[i]<<" "<<dfn[mem[i]]<<" "<<dfn2[mem[i]]<<endl;
                id[++num]=dfn[mem[i]];id[++num]=dfn2[mem[i]];
            }
            sort(id+1,id+num+1);
            num=unique(id+1,id+num+1)-id-1;
            
            top=0;
            cnt=0;
            x=0;
            for(reg i=1;i<=num;++i){
                if(fdfn[id[i]]>0){
                    x=fdfn[id[i]];
                    if(top) add(sta[top],x,fin(x,sta[top]));
                    sta[++top]=x;    
                }else{
                    sta[top--]=0;
                }
            }
            sol(1,0);
            
            printf("%lld
    ",f[1]);
            
            
            for(reg i=1;i<=tot;++i){
                has[mem[i]]=0;
                hd[mem[i]]=0;
            }
            cnt=0;
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/1/31 12:55:30
    */

    总结:

    思路还是直观自然的

    我们发现实际用到的点不是很多。考虑值保留关键点。就用到了虚树

    虚树的建立,这里欧拉序起到了很大的作用。

    因为有欧拉序是可以还原出来一个树的

    没有怎么接触过的

  • 相关阅读:
    python学习之路基础篇(第八篇)
    python学习之路基础篇(第七篇)
    python学习之路基础篇(第六篇)
    python学习之路基础篇(第五篇)
    python学习之路基础篇(第四篇)
    目标检测——IoU 计算
    MXNet 中的 hybird_forward 的一个使用技巧
    Anchor 的两种编程实现
    我的目标检测笔记
    动手创建 SSD 目标检测框架
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10347241.html
Copyright © 2020-2023  润新知