zoukankan      html  css  js  c++  java
  • bzoj5333: [Sdoi2018]荣誉称号

    请不要去改题目给的输入,不然你会wa穿。。。

    这么故弄玄虚的题目,肯定要先转换问题

    看到这个不断的除2想起别人家的线段树的写法。。。x的两个孩子是x<<1和x<<1|1

    然后问题就转换成给你一棵树,你可以增加树的权值,要让树上每一条长度为k+1的链上的点权和%m都等于0

    先%m把取值范围降到[0,m-1]

    观察一下性质,假如通过加权确定了根节点的点权=d,就确定了所有和它距离为k+1的点的点权必须也要变成d

    据此我们把点分成k+1组,那是不是每一组的点权都要变成相同的呢?

    然而并不,正确的答案应该是这棵树上面的前2^(k+1)-1个点,它们的点权是不受约束的,然后它们覆盖了下面的所有点

    我们可以处理出一个c数组,表示第i组全部改成j的花费

    然后就是裸的树包了

    然而暴力处理c只能得到70分的好成绩,能不能再优化一下呢

    我想法是上一个线段树,然而其实可以先把ci,0处理出来,然后DP出其他的值

    ci,j=ci,j-1+∑bk(k属第i组)  - m*∑u(u属第i组且初始值为u)bu

    两个∑都是可以预处理的,所以是O(2^k*m)的复杂度

    背包O(2^k*m^2)没什么毛病

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    
    int n,k,m,Bin[30];LL a[10001000],b[10001000];
    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%d%d%u%u%u%d%d", &n, &k, &m, &p, &SA, &SB, &SC, &A, &B);
        for(int i = 1; i <= p; i++)scanf("%lld%lld", &a[i], &b[i]);
        for(int i = p + 1; i <= n; i++){
            a[i] = LL(rng61() % A + 1);
            b[i] = LL(rng61() % B + 1);
        }
    }
    
    //-----------------------------scanf-----------------------------------------
    
    LL c[4100][210],sum[4100];//和i点合并,一起变成j的费用cij
    void dfs(int x)
    {
        if(x>n)return ;
        
        int f=x;
        while(f/Bin[k]>0)f/=Bin[k];
        
        if(a[x]!=0)
        {
            if(f==51) 
            {
                int t;t++;
            }
            c[f][0]+=(LL(m)-a[x])*b[x];
            c[f][a[x]]-=b[x]*m;
        }
        sum[f]+=b[x];
        
        dfs(x<<1);
        dfs(x<<1|1);
    }
    void getc()
    {
        for(int i=1;i<Bin[k];i++)
            for(int j=1;j<m;j++)
                c[i][j]+=c[i][j-1]+sum[i];
    }
    
    //------------------------------------init----------------------------------------------------
    
    LL f[4100][210];
    void treeDP(int x)
    {
        if(x*2>=Bin[k])
        {
            memcpy(f[x],c[x],sizeof(f[x]));
            return ;
        }
        
        int lc=x<<1,rc=x<<1|1;
        treeDP(x<<1);
        treeDP(x<<1|1);
        
        for(int i=0;i<m;i++)
            for(int j=0;j<m;j++)
            {
                int u=(i-j+m)%m;
                f[x][i]=min(f[x][i],f[lc][u]+f[rc][u]+c[x][j]);
            }
    }
    
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        Bin[0]=1;for(int i=1;i<=25;i++)Bin[i]=Bin[i-1]*2;
        int T;
        scanf("%d",&T);
        while(T--)
        {
            gen();k++;
            for(int i=1;i<=n;i++)a[i]%=m;
            memset(c,0,sizeof(c));
            memset(sum,0,sizeof(sum));
            dfs(1);
            getc();
            
            memset(f,63,sizeof(f));
            treeDP(1);
            printf("%lld
    ",f[1][0]);
        }
        return 0;
    }
  • 相关阅读:
    2014年广州区域赛k题解
    2014年广州区域赛e题解
    2014年广州区域赛i题解
    最大化平均值问题
    codeforces 976e 题解
    maven
    机器学习入门
    拟合
    插值
    熵权法
  • 原文地址:https://www.cnblogs.com/AKCqhzdy/p/10218659.html
Copyright © 2011-2022 走看看