• Luogu P3177 [HAOI2015]树上染色


    一道有机结合了计数贪心这一DP两大考点的神仙题,不得不说做法是很玄妙。

    首先我们很容易想到DP,设(f_{i,j})表示在以(i)为根节点的子树中选(j)个黑色节点的最大收益值。

    然后我们考虑那种暴力转移就是那种看上去是(O(n^3))实际经严格证明后时(O(n^2))的DP

    然后推推推推推推,一个小时过去还是一个屁

    这个时候我们不禁质疑,这个鬼状态不会是错的吧。

    没错,它就是错的,因为这样对于你子树上面的黑点节点之间的收益你都一无所知

    然后我们联想到另外一道树上计数的题目:51Nod 1677 treecnt&&sol,然后我们又是单独考虑每一条边的贡献

    再仔细推一波可以发现一条边对于黑白点的贡献之和两边黑白点的个数有关,和具体的结构鸟关系都没有。

    于是我们换一波方程,设(f_{i,j})表示在以(i)为根节点的子树中选(j)个黑色节点对总答案的贡献

    然后我们枚举子树中黑色点的数量然后一个类似于背包的转移即可。

    具体看CODE

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    using namespace std;
    const int N=2005;
    struct edge
    {
        int to,next,v;
    }e[N<<1];
    int head[N],size[N],n,k,cnt,x,y,z,rt=1;
    long long f[N][N];
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch; while (!isdigit(ch=tc()));
        while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void double_add(int x,int y,int z)
    {
        e[++cnt].to=y; e[cnt].next=head[x]; e[cnt].v=z; head[x]=cnt;
        e[++cnt].to=x; e[cnt].next=head[y]; e[cnt].v=z; head[y]=cnt;
    }
    inline void maxer(long long &x,long long y)
    {
        if (y>x) x=y;
    }
    inline int min(int a,int b)
    {
        return a<b?a:b;
    }
    inline void DFS(int now,int fa)
    {
        register int i,j,s,x; size[now]=1;
        f[now][0]=f[now][1]=0;
        for (i=head[now];~i;i=e[i].next)
        if (e[i].to!=fa) DFS(e[i].to,now),size[now]+=size[e[i].to];
        for (i=head[now];~i;i=e[i].next)
        if (e[i].to!=fa) for (j=min(k,size[now]);j>=0;--j)
        {
            for (s=0,x=min(j,size[e[i].to]);s<=x;++s)
        	maxer(f[now][j],f[e[i].to][s]+f[now][j-s]+1LL*e[i].v*(1LL*s*(k-s)+1LL*(size[e[i].to]-s)*(n-k-size[e[i].to]+s)));
        }
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i; read(n); read(k);
        if (2*k>n) k=n-k;
        memset(head,-1,sizeof(head)); memset(f,167,sizeof(f));
        for (i=1;i<n;++i)
        read(x),read(y),read(z),double_add(x,y,z);
        DFS(rt,-1); return printf("%lld",f[rt][k]),0;
    }
    

    注意上面的一个小trick

    if (2*k>n) k=n-k;
    

    这样对无关的常数浪费就会大大降低直接帮助我卡过了BZOJ的老爷机,不加T死

  • 相关阅读:
    Webstorm 下的Angular2.0开发之路
    利用hexo+github+nodejs搭建自我博客的一天
    滑稽的下午angularjs 2.0管道的使用
    DNGuard 标准版 v2.90发布
    DNGuard Enterprise v2.92 released
    C#复杂表达式的问题
    直接在.Net程序(C#)中执行 native code
    采用Native 引导方式的.Net加密保护
    DNGuard HVM Trial V2.82 发布
    DNGuard Enterprise v2.90 released
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9374216.html
Copyright © 2020-2023  润新知