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\n",f[1][0]);
    }
    return 0;
}

 

posted @ 2019-01-04 10:59  AKCqhzdy  阅读(179)  评论(0编辑  收藏  举报