zoukankan      html  css  js  c++  java
  • bzoj2286: [Sdoi2011]消耗战

    估计像我一样想树形DP搞搞不想时间复杂度的蒟蒻很少见了~~~~

    然而花了一个下午学会了——虚树!!!厉害!(感觉在竞赛室都在不停的get新姿势,舒服)

    这个东西呢主要是省略一些不会访问到的点。比如这题的样例第一个问,我们就把树变成这样:

    就是保留根,通过保留lca和要断的节点来把树的形状得出。

    具体的做法,就是弄一个单调栈,栈头到栈尾由浅至深↓

    然后,找到lca,如图,那我们这时就可以将sta[top]和sta[top-1]的边建好,然后踢栈顶,接着让新的sta[top]和lca的边建好,然后将lca和p[i]进栈,最后得到的就是虚树了。

    细节:如果在while里面每次都重置一遍elast,时间就还是O(nm),所以就要在DP的时候顺便清空,还有inf要开大。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    LL inf;
    int Bin[25];
    struct node
    {
        int x,y,d,next;
    }a[510000],e[510000];int len,last[310000],elen,elast[310000];
    void ins(int x,int y,int dd)
    {
        len++;
        a[len].x=x;a[len].y=y;a[len].d=dd;
        a[len].next=last[x];last[x]=len;
    }
    void ins2(int x,int y)
    {
        elen++;
        e[elen].x=x;e[elen].y=y;
        e[elen].next=elast[x];elast[x]=elen;
    }
    int cnt,pos[310000];
    LL v[310000];
    int dep[310000],f[25][310000];
    void dfs(int x,int fa)
    {
        pos[x]=++cnt;
        f[0][x]=fa;
        for(int i=1;Bin[i]<=dep[x];i++)f[i][x]=f[i-1][f[i-1][x]];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y!=fa)
            {
                if(v[x]<a[k].d)v[y]=v[x];
                else v[y]=a[k].d;
                
                dep[y]=dep[x]+1;
                dfs(y,x);
            }
        }
    }
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])swap(x,y);
        
        for(int i=20;i>=0;i--)
            if(dep[x]-dep[y]>=Bin[i])x=f[i][x];
        if(x==y)return x;
        
        for(int i=20;i>=0;i--)
            if(dep[x]>=Bin[i]&&f[i][x]!=f[i][y]){x=f[i][x];y=f[i][y];}
        return f[0][x];
    }
    bool cmp(int n1,int n2)
    {
        if(pos[n1]<pos[n2])return true;
        return false;
    }
    int k,p[310000];
    int top,sta[310000];//sta表示在已经建完毕的虚树上,以最后一个插入的点为端点的DFS链
    void virtual_tree()
    {
        sort(p+1,p+k+1,cmp);
        //按dfs序大到小排序,对于任意点,dfs序大于该点值的点要么处于它的子树中,要么在另一棵同级的子树 
        int tp=1;//如果p[i]是处于p[tp]的子树里,只要p[tp]断了,p[i]同样断,so无视 
        for(int i=2;i<=k;i++)
            if(LCA(p[i],p[tp])!=p[tp])p[++tp]=p[i];
        k=tp;
            
        top=0;sta[++top]=1;
        for(int i=1;i<=k;i++)
        {
            int lca=LCA(p[i],sta[top]);
            while(1)//不停往上跳令dep[sta[top]]>dep[lca]>dep[sta[top-1]]
            {
                if(dep[lca]>=dep[sta[top-1]])//找到 
                {
                    if(lca!=sta[top])ins2(lca,sta[top]);//下面的连向lca,然后没用了 
                    top--;
                    if(lca!=sta[top])sta[++top]=lca; 
                    break;
                }
                ins2(sta[top-1],sta[top]);top--;//往上跳时顺便将下面的连在一起 
            }
            if(p[i]!=sta[top])sta[++top]=p[i];
        }
        top--;
        while(top>0){ins2(sta[top],sta[top+1]);top--;}
    }
    LL dp[310000];
    void treeDP(int x)
    {
        LL ans=0;dp[x]=v[x];
        for(int k=elast[x];k;k=e[k].next)
        {
            int y=e[k].y;
            treeDP(y);
            ans+=dp[y];
        }
        elast[x]=0;
        if(ans!=0)dp[x]=min(dp[x],ans);
    }
    int main()
    {
        //freopen("repair.in","r",stdin);
        //freopen("repair.out","w",stdout);
        
        inf=1;for(int i=1;i<=60;i++)inf*=2;
        Bin[0]=1;for(int i=1;i<=25;i++)Bin[i]=Bin[i-1]*2;
        
        int n,x,y,dd;
        scanf("%d",&n);
        len=0;memset(last,0,sizeof(last));
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&x,&y,&dd);
            ins(x,y,dd);ins(y,x,dd);
        }
        cnt=0;dep[1]=1;v[1]=inf;dfs(1,0);
        
        int m;
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d",&k);
            for(int i=1;i<=k;i++)scanf("%d",&p[i]);
            elen=0;
            virtual_tree();
            treeDP(1); 
            printf("%lld
    ",dp[1]);
        }
        return 0;
    }
  • 相关阅读:
    递归算法详解
    树、森林和二叉树的转换
    C++ 类的静态成员详细讲解
    C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
    C++中的static关键字的总结
    C/C++中static关键字详解
    配置文件
    Spring Boot 注释
    使用 Spring Boot 快速构建 Spring 框架应用
    Myeclipse快捷键(二)
  • 原文地址:https://www.cnblogs.com/AKCqhzdy/p/7647574.html
Copyright © 2011-2022 走看看