zoukankan      html  css  js  c++  java
  • 【BZOJ5333】荣誉称号(SDOI2018)-找规律+树形DP

    测试地址:荣誉称号
    做法:本题需要用到找规律+树形DP。
    第一次想出Luogu黑题祭。
    首先,考虑题目中条件的形式,如果我们令点i的父亲为点i2(实际上就是在二进制中右移一位),那么所有点可以拼成一棵二叉树,问题就可以表示成:要使树上所有长为k+1的祖孙链上的a值之和对m的余数为0,点ia值加1的花费是bi,求最小花费。
    因为要使所有长为k+1的祖孙链上的a值之和对m的余数为0,那么每一个深度为x的点的两个儿子a值应该同余(因为从根到两个儿子的路径对m余数都为0)。进一步地向下推,我们可以得到一个结论:每个点的a值和它的k+1级父亲的a值同余。也就是说,在决定前k+1层的某一个点时,顺带着也决定了它所有x(k+1)级儿子的值,这时候我们就可以把k+1层以下的点的贡献直接缩到前k+1层的点上去。以下为了方便讨论,直接令a值为实际a值对m的余数。
    cost(i,j)为点i(1x<2k+1)选择的a值为j时所连带产生的贡献。如果暴力算出这个数组,那么时间复杂度将是O(nm),无法承受。事实上,我们可以尝试先O(n)求出cost(i,0),然后进行递推。要从cost(i,j)cost(i,j+1),首先是所有点都要多增加费用,然后对于那些原本a值就是j+1的点,要减去mbv的贡献。于是我们只需要在O(n)cost(i,0)时,顺带求出totsum(i):点i连带的点的费用总和,sum(i,j):点i连带的原a值为j的点的费用总和,就可以对每个i进行O(m)递推了,那么总的时间复杂度就是O(n+2k+1m),可以承受。
    那么接下来考虑树形DP。对于所有第k层的点,它的两个儿子要取的a值和它到根路径上a的和有关,于是我们这样定义状态:令f(i,j)为点i的子树以及所有连带的点中,在根到点i路径上a值之和的余数为j时,所能得到的最小花费。因为DP时的方便,我们在求f(i,j)时才枚举两个儿子要选什么,所以这个f(i,j)的贡献中不包含i及其连带点的贡献。那么对于所有第k层的点有:
    f(i,j)=cost(lson,mj)+cost(rson,mj)
    其中mj应该再对m取模,以下这个位置的式子都应该取模,为了方便就不写了。
    而对于前k1层的点有:
    f(i,j)=min{f(lson,j+x)+cost(lson,x)+f(rson,j+y)+cost(rson,y)}
    显然x,y的选择互不影响,所以直接O(m)分别求出和x,y有关的部分式子的最小值再加起来即可。
    而最后的答案为:
    ans=min{f(1,x)+cost(1,x)}
    就是加上了点1及其连带点的贡献。于是DP的复杂度为O(2km2),可以承受,我们就解决了这一题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll N=20000010;
    const ll K=2060;
    const ll M=1010;
    const ll inf=1000000000ll*1000000000ll;
    int T,n,k;
    ll m,a[N],b[N],pre[N];
    ll totsum[K],sum[K][M],cost[K][M];
    ll f[K][M];
    
    unsigned int SA, SB, SC;
    int p, A, B;
    
    unsigned int rng61(){
        SA ^= SA << 16;
        SA ^= SA >> 5;
        SA ^= SA << 1;
        unsigned int t = SA;
        SA = SB;
        SB = SC;
        SC ^= t ^ SA;
        return SC;
    }
    
    void gen(){
        scanf("%d%d%lld%d%u%u%u%d%d", &n, &k, &m, &p, &SA, &SB, &SC, &A, &B);
        for(int i = 1; i <= p; i++)scanf("%d%d", &a[i], &b[i]);
        for(int i = p + 1; i <= n; i++){
            a[i] = rng61() % A + 1;
            b[i] = rng61() % B + 1;
        }
    }
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            memset(a,0,sizeof(a));
            memset(b,0,sizeof(b));
    
            gen();
    
            memset(totsum,0,sizeof(totsum));
            memset(sum,0,sizeof(sum));
            memset(cost,0,sizeof(cost));
    
            int tot=1;
            while(tot<=n) tot<<=1;
            n=tot;
            for(int i=1;i<=n;i++)
            {
                pre[i]=(i>>(k+1))?pre[i>>(k+1)]:i;
                a[i]%=m;
                totsum[pre[i]]+=b[i];
                sum[pre[i]][a[i]]+=b[i];
                cost[pre[i]][0]+=(m-a[i])%m*b[i];
            }
            for(int i=1;i<(1<<(k+1));i++)
                for(int j=1;j<m;j++)
                    cost[i][j]=cost[i][j-1]+totsum[i]-m*sum[i][j];
    
            for(int i=(1<<(k-1));i<(1<<k);i++)
                for(int j=0;j<m;j++)
                    f[i][j]=cost[i<<1][(m-j)%m]+cost[i<<1|1][(m-j)%m];
            for(int i=(1<<(k-1))-1;i>=1;i--)
                for(int j=0;j<m;j++)
                {
                    ll ans1=inf,ans2=inf;
                    for(int x=0;x<m;x++)
                        ans1=min(ans1,f[i<<1][(j+x)%m]+cost[i<<1][x]);
                    for(int x=0;x<m;x++)
                        ans2=min(ans2,f[i<<1|1][(j+x)%m]+cost[i<<1|1][x]);
                    f[i][j]=ans1+ans2;
                }
    
            ll ans=inf;
            for(int i=0;i<m;i++)
                ans=min(ans,f[1][i]+cost[1][i]);
            printf("%lld
    ",ans);
        }
    
        return 0;
    }
  • 相关阅读:
    XPOSED优秀模块列表 E假Goto免费
    XPOSED优秀模块列表 即时屏幕开启
    介绍一个国外出售kali nethunter网站
    XPOSED优秀模块列表 数据使用
    XPOSED优秀模块列表 无自动快捷方式
    XPOSED优秀模块列表 直接APK安装
    XPOSED优秀模块列表 隐藏模拟位置
    XPOSED优秀模块列表 Tinder Mods
    XPOSED优秀模块列表 PIN/模式快捷方式
    XPOSED优秀模块列表 锁屏禁用器
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793277.html
Copyright © 2011-2022 走看看