【BZOJ5333】荣誉称号(动态规划)

【BZOJ5333】荣誉称号(动态规划)

题面

BZOJ
洛谷

题解

今天早上贱狗老师讲的。然而我还是不会。
只好照着\(zsy\)代码大力理解一波。
首先观察等式,如果比较熟悉线段树,会发现就是线段树的前\(k\)个祖先
而线段树是完全二叉树,也就所有东西形成了一个完全二叉树。
并且任意节点和它的前\(k\)次祖先的和都要是\(0\)(以下都是在模\(m\)意义下)
所以,我们可以轻易推出一个结论,\(x\)节点和\(x\)\(k\)次祖先同余。
所以,我们只需要考虑前\(k\)层就好了,剩下的点全部可以按照同余的关系归并到了一起。
这样子节点个数就从\(10^7\)降到了\(2^{11}\)
现在也就是任意一个叶子节点到根节点的和都是要\(0\)
那么直接\(dp\)
\(f[i][j]\)表示第\(i\)个节点到达它所有儿子的路径和都是\(j\)的最小代价。
转移的时候考虑一下儿子的权值是多少以及当前点是多少。
当前点变成某个权值的代价可以提前预处理。
这样子复杂度就是\(O(2^km^2)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define MAX 11111111
#define W 2050
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;
}
int n,k,m,a[MAX],b[MAX],fa[MAX];
ll val[W][200],sum[W],cal[W][200],f[W][200];
void init()
{
    memset(val,0,sizeof(val));memset(sum,0,sizeof(sum));
    memset(cal,0,sizeof(cal));memset(f,63,sizeof(f));
    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("%d%d",&a[i],&b[i]);
    for(int i=p+1;i<=n;i++)a[i]=rng61()%A+1,b[i]=rng61()%B+1;
    for(int i=n+1;i<(1<<(k+1));++i)a[i]=b[i]=0;n=max(n,(1<<(k+1))-1);
    for(int i=1;i<=n;++i)
    {
        a[i]%=m;
        if(i<(1<<(k+1)))fa[i]=i;
        else fa[i]=fa[i>>(k+1)];
        val[fa[i]][0]+=a[i]?b[i]*(m-a[i]):0;
        sum[fa[i]]+=b[i];cal[fa[i]][a[i]]+=b[i]*m;
    }
    for(int i=1;i<(1<<(k+1));++i)
        for(int j=1;j<m;++j)
            val[i][j]=val[i][j-1]+sum[i]-cal[i][j];
    
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        init();
        for(int i=1<<k;i<(1<<(k+1));++i)
            for(int j=0;j<m;++j)f[i][j]=val[i][j];
        for(int i=(1<<k)-1;i;--i)
            for(int j=0;j<m;++j)
                for(int l=0;l<m;++l)
                    f[i][j]=min(f[i][j],f[i<<1][(j-l+m)%m]+f[i<<1|1][(j-l+m)%m]+val[i][l]);
        printf("%lld\n",f[1][0]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/cjyyb/p/9255553.html