• Codeforces contest 856 D(树形DP,树剖)


    超链接:http://codeforces.com/contest/856/problem/D

    题意就是给你一棵树,和一些树上的链,每个链有一个权值,要求求一个不相交链的集合,使得权值最大.

    两条链相交定义为这两条链有公共点.

    那么这题非常的难,于是我不会做......

    咳咳咳.......

    我于是就学了一发正解.

    f[i]表示在以i为根的子树中的答案(仅能包含在完全在子树内的链)

    那么把链集中压到两端点lca的vector里去.

    设sum[i]=sigma(f[son[i]])

    很好理解

    f[i]=max(sum[i],对每条在i的vector里的链的可能答案);

    那么怎么计算后者呢?

    如图:

    无标题

    现在要更新f[1]

    红色的链为考虑的链,那么它的可能答案就是f[5]+f[6]+f[7]+f[11]+1

    变形一下就是sum[4]+sum[3]-f[4]+sum[2]-f[3]+sum[1]-f[2]+sum[10]+sum[1]-f[10]-sum[1]+1

    sum[1]加了两遍,所以要减去一次.

    那么你就可以根据这个写出DP了

    复杂度是O(n+m)(Tanjan LCA)+O(n*链的期望长度)(树形DP)

    但是这样只能过随机数据,不能过hack数据.

    于是考虑树剖优化.

    维护contribution[i]表示从这条重链底端到它的sigma(sum[i]-f[son[i]])(此处son[i]表示重儿子)

    每次跳的时候直接到链顶.

    再乱计算一波就可以了.

    贴上我的乱计算代码:

    int calc(int i,int x){ int y=u[i],z=v[i],res=sum[y]+sum[z]; while (top[y]!=top[x]) res+=contribution[top[y]]-contribution[y]+sum[fa[top[y]]]-dp[top[y]],y=fa[top[y]]; res+=contribution[x]-contribution[y]; while (top[z]!=top[x]) res+=contribution[top[z]]-contribution[z]+sum[fa[top[z]]]-dp[top[z]],z=fa[top[z]]; res+=contribution[x]-contribution[z]; return res-sum[x]+money[i]; }i是链的编号,top是链顶,money是权值.然后这题就可以过了.悲伤的是我竟然写了一个tanjan lca 其实可以沿用树剖.

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=200010;
    int n,m,x,y,k,ancestor[N],fa[N],fi[N],u[N],v[N],sum[N],dp[N],ne[N<<1],b[N<<1],money[N],sz[N],top[N],son[N],contribution[N];
    vector<pair<int,int> >g[N];
    vector<int>w[N];
    inline int read() {
        char ch=getchar(); int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while('0'<=ch&&ch<='9'){
            x=x*10+ch-'0';
            ch=getchar();
        }
        return x;
    }
    inline void add(int x,int y){b[++k]=y; ne[k]=fi[x]; fi[x]=k;}
    inline int ask(int x){return x==ancestor[x]?x:ancestor[x]=ask(ancestor[x]);}
    inline int ask2(int x){return x==top[x]?x:top[x]=ask2(top[x]);}
    inline void dfs(int x){
        sz[x]=1;
        for (int j=fi[x]; j; j=ne[j]){
            fa[b[j]]=x;
            dfs(b[j]);
            ancestor[b[j]]=x;
            sz[x]+=sz[b[j]];
            if (sz[b[j]]>sz[son[x]]) son[x]=b[j];
        }
        top[son[x]]=x;
        for (vector<pair<int,int> >::iterator it=g[x].begin(); it!=g[x].end(); it++)
            if (it->second==1||fa[it->second]) w[ask(it->second)].push_back(it->first);
    }
    int calc(int i,int x){
        int y=u[i],z=v[i],res=sum[y]+sum[z];
        while (top[y]!=top[x]) res+=contribution[top[y]]-contribution[y]+sum[fa[top[y]]]-dp[top[y]],y=fa[top[y]];
        res+=contribution[x]-contribution[y];
        while (top[z]!=top[x]) res+=contribution[top[z]]-contribution[z]+sum[fa[top[z]]]-dp[top[z]],z=fa[top[z]];
        res+=contribution[x]-contribution[z];
        return res-sum[x]+money[i];
    }
    void getans(int x){
        for (int j=fi[x]; j; j=ne[j]){
            getans(b[j]);
            sum[x]+=dp[b[j]];
        }
        dp[x]=sum[x];
        contribution[x]=contribution[son[x]]+sum[x]-dp[son[x]];
        for (vector<int>::iterator it=w[x].begin(); it!=w[x].end(); it++) dp[x]=max(dp[x],calc(*it,x));
    }
    int main(){
        n=read(); m=read();
        for (int i=2; i<=n; i++) add(read(),i);
        for (int i=1; i<=m; i++){
            u[i]=read(); v[i]=read(); money[i]=read();
            g[u[i]].push_back(make_pair(i,v[i]));
            g[v[i]].push_back(make_pair(i,u[i]));
        }
        for (int i=1; i<=n; i++) ancestor[i]=top[i]=i;
        dfs(1);
        for (int i=1; i<=n; i++) ask2(i);
        for (int i=1; i<=n; i++) w[i].erase(unique(w[i].begin(),w[i].end()),w[i].end());
        getans(1);
        printf("%d",dp[1]);
    }

    不知道是不是我写炸了,当两个端点有父子关系或相等时,tajan lca 会把一个lca压入vector两次.于是有奇怪的话

    for (int i=1; i<=n; i++) w[i].erase(unique(w[i].begin(),w[i].end()),w[i].end());

    纯属作死.

    当然不加这句话也没事,只会让程序变慢而已.

     调代码
  • 相关阅读:
    20165323 我期望的师生关系
    20165311 第六周学习总结
    20165311 实验一 Java开发环境的熟悉
    第四周学习总结
    第三周 学习总结
    20165311 预备作业3 Linux安装及学习
    20165336 2017-2018-2 《Java程序设计》第5周学习总结
    20165336 2017-2018-2 《Java程序设计》第4周学习总结
    20165336 2017-2018-2 《Java程序设计》第3周学习总结
    20165336 预备作业3 Linux安装及学习
  • 原文地址:https://www.cnblogs.com/Yuhuger/p/7616027.html
Copyright © 2020-2023  润新知