zoukankan      html  css  js  c++  java
  • 虚树+【BZOJ2286】【SDOI2011】消耗战(虚树)(DP)

    先看一道题:

    【BZOJ2286】【SDOI2011】消耗战

    Description
    在一场战争中,战场由n个岛屿和n−1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
    侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。
    Input
    第一行一个整数n,代表岛屿数量。
    接下来n−1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连。
    第n+1行,一个整数m,代表敌方机器能使用的次数。
    接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。
    Output
    输出有m行,分别代表每次任务的最小代价。
    Sample Input
    10
    1 5 13
    1 9 6
    2 1 19
    2 4 8
    2 3 91
    5 6 8
    7 5 4
    7 8 31
    10 7 9
    3
    2 10 6
    4 5 7 8 3
    3 9 4 6
    Sample Output
    12
    32
    22
    HINT
    对于100%的数据,2≤n≤250000,m≥1,∑ki≤500000,1≤ki≤n−1,1≤u,v≤n,1≤c≤100000

    可以想到一个树形DP,(dp[i])表示切断在以i为根的子树中所有关键点(即题中资源丰富的岛屿)所需的最小代价。

    这样的树形DP跑一次当然是(O(n))的,总复杂度为(O(NM)),肯定过不了。

    但是模拟一下可以发现,有一些点的dp值是完全对答案没有贡献的(例如子树中不包含关键点),但是却要计算它的dp值,这样就增加了算法的时间复杂度。

    那这样可以思考一下,能不能去掉这些没有用的点,使得树形DP的树尽量小,从而达到减少时间复杂度的效果?

    显然可以。

    记录一个从每一个点到根路径上权值最小的边,这时对答案真正有贡献的是所有关键点以及它们的LCA,以及它们LCA的LCA……,将这些节点抽出来,加上一个1号节点为根,再在这颗新建的树上面跑DP即可。

    然而实际上可以不用连边,记录每个点在dfs里面进出栈的顺序,每个点变成表示出栈和进栈的点,拿一个栈进行模拟即可,顺带可以清空。

    这样的时间复杂度是多少?

    因为每次本身有关键节点,再加上LCA,总空间复杂度是(O(2*∑ki)),时间复杂度是(O(M{log_{2*∑ki}}))

    (----------------------------------------------)

    2019.11.30:

    做了几道题后发现像上面这样做会在需要多次便利时很麻烦,而且因为没用树剖求LCA,所以搞得程序很慢,所以换一种建虚树的方法。

    同样的,我们是要求出每个点的dfn序,将所有节点按照dfn序排列。

    然后用一个栈来维护根到栈顶元素的路径上的点。

    假设新加入一个节点p,栈顶元素为(st[top]),lca为x和p的最近公共祖先。

    因为是按照dfn序排列,所以p不是lca,

    1.如果x是lca,直接将p入栈即可。

    2.如果x不是lca,则p和x不在同一棵子树中,且x的子树已经遍历完了(因为x的子树中若有y未被遍历,那么y一定在p之前被遍历)

    开始构建:

    设栈顶中第二个元素为y,分三种情况讨论:

    1.dfn[y]>dfn[lca]

    连y到x的边(y先放肯定y在x上面),x出栈

    2.dfn[y]==dfn[lca]

    y等于lca。

    连lca到x的边,子树的构建完成。

    3.dfn[y]<dfn[lca]

    lca在y和x之间,连lca到x的边,x出栈,lca入栈

    最后将p入栈即可。

    不断重复进行这个过程,就可以构建虚树了。

    就给个构建树的函数吧

    void insert(int x)
    {
    	if(top==1)
    	{
    		st[++top]=x;
    		return;
    	}
    	int LCA=lca(x,s[top]);
    	if(LCA==s[top])
    	{
    		st[++top]=x;
    		return;
    	}
    	while(top>1&&in[st[top-1]]>=in[LCA])
    	{
    		adde1(st[top-1],st[top]);
    		top--;
    	}
    	if(LCA!=st[top])
    	{
    		adde1(LCA,st[top]);
    		st[top]=LCA;
    	}
    	st[++top]=x;
    }
    

    下面的代码就用栈模拟吧。

    #include<bits/stdc++.h>
    #define N 500010
    #define inf 10000010
    using namespace std;
    int in[N],to[N<<1],nxt[N<<1],cost[N<<1],head[N],out[N],ans[N],deep[N],fa[N][20],indew,cnt,nct,n,m,x,y,z,tree[N];
    long long dp[N];
    bool is[N];
    stack<int> s;
    void adde(int x,int y,int z)
    {
        to[++cnt]=y;
        nxt[cnt]=head[x];
        cost[cnt]=z;
        head[x]=cnt;
    }
    void dfs(int u)
    {
        for(int i=1;i<=19;i++)
        {
            fa[u][i]=fa[fa[u][i-1]][i-1];
        }
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i],w=cost[i];
            if(!deep[v])
            {
                in[v]=++indew;//进栈序
                deep[v]=deep[u]+1;
                ans[v]=min(ans[u],w);
                fa[v][0]=u;
                dfs(v);
            }
        }
        out[u]=++indew;//出栈序
    }
    bool cmp(int x,int y)//按dfn序进行排序
    {
        return ((x>0)?in[x]:out[-x])<((y>0)?in[y]:out[-y]);
    }
    int lca(int x,int y)
    {
        if(deep[x]<deep[y])
        {
            swap(x,y);
        }
        for(int i=19;i>=0;i--)
        {
            if(deep[fa[x][i]]>=deep[y])
            {
                x=fa[x][i];
            }
        }
        if(x==y)
        {
            return x;
        }
        for(int i=19;i>=0;i--)
        {
            if(fa[x][i]!=fa[y][i])
            {
                x=fa[x][i];
                y=fa[y][i];
            }
        }
        return fa[x][0];
    }
    int main()
    {
    //  freopen("1.txt","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            adde(x,y,z);
            adde(y,x,z);
        }
        ans[1]=inf;
        deep[1]=1;
        dfs(1);
        scanf("%d",&m);
        while(m--)
        {
            nct=0;
            scanf("%d",&x);
            while(x--)//以下是建虚树
            {
                scanf("%d",&tree[++nct]);
                dp[tree[nct]]=ans[tree[nct]];
                is[tree[nct]]=1;
            }
            sort(tree+1,tree+nct+1,cmp);//先按dfn排序
            for(int i=2;i<=nct;i++)//放LCA进树中
            {
                int LCA=lca(tree[i],tree[i-1]);
                if(!is[LCA])
                {
                    tree[++nct]=LCA;
                    is[LCA]=1;
                }
            }
            int nct1=nct;
            for(int i=1;i<=nct1;i++)//加出栈点
            {
                tree[++nct]=-tree[i];
            }
            if(!is[1])
            {
                tree[++nct]=1;
                tree[++nct]=-1;
                is[1]=1;
            }
            sort(tree+1,tree+nct+1,cmp);
            for(int i=1;i<=nct;i++)//用栈模拟
            {
                if(tree[i]>0)
                {
                    s.push(tree[i]);
                }else{
                    int v=s.top(),u;//v是儿子,v出栈后,u就是父亲
                    s.pop();
                    if(v==1)
                    {
                        printf("%lld
    ",dp[1]);
                        break;
                    }
                    u=s.top();
                    dp[u]+=min(dp[v],1ll*ans[v]);//dp
                    dp[v]=is[v]=0;
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    03、CPU主频,和性能
    02、计算机组成原理相关知识
    常用正则表达式,手机号、固话号、身份证号等
    01、计算机原理结构,及冯诺依曼体系结构
    7-7 Complete Binary Search Tree (30分) 完全二叉搜索树
    7-2 Reversing Linked List (25分)
    7-1 Maximum Subsequence Sum (25分)
    6-17 Shortest Path [4] (25分)
    6-16 Shortest Path [3] (25分)
    6-15 Iterative Mergesort (25分)
  • 原文地址:https://www.cnblogs.com/2017gdgzoi44/p/11949590.html
Copyright © 2011-2022 走看看