• BZOJ5333:[SDOI2018]荣誉称号——题解


    https://www.lydsy.com/JudgeOnline/problem.php?id=5333

    https://www.luogu.org/problemnew/show/P4620

    题意见上。

    如果想看官方题解的话,移步:http://www.cnblogs.com/clrs97/p/9064630.html

    如果你第一眼没看懂的话,没关系,往下看吧。

    应该不难发现a数组构成了一棵有向完全二叉树的形态,于是题意转化成树上点数为k+1的路径点和%m=0。

    并且会发现路径会重叠,且当一条路径的1~k个点和另一条路径的2~k+1个点重合时,第一条路径的k+1的点的权值a和第二条路径的1的点的权值b显然要满足限制:

    a%m=b%m

    并且发现这个限制只对到根路径长为k的点(起个名字叫关键点)不适用换句话讲除了这些点以外的点我们都可以扔掉了,因为除此之外的点的答案都可以通过关键点推算出来。

    于是我们只需要对关键点进行dfs就行啦!并且因为到根路径长为k,所以我们只需要统计根到所有合法叶子的路径就行了

    设f[i][j]表示i子树中i到叶子的路径答案%m为j时,在原树上的最小代价。(换句话讲,我们要求的就是f[1][0])

    再设w[i][j]表示与i点满足限制的点的点权为j的最小代价。

    则对于合法的点,f[x][(i+j)%m]=w[x][i]+f[x*2][j]+f[x*2+1][j],不合法的点比如非关键点,或是该点最深叶子到根距离小于k的,需要另行特判。

    那么关键就是求w数组了,暴力显然是O(nm)的,于是我们有一种差分的思想去求这个w数组。

    首先求出所有与x点满足性质点i的价值和sum[x]+=b[i]。

    然后O(n)求出w[x][0]+=(m-a[i])*b[i]。再之后O(2^k*m)求出w[i][j]+=w[i][j-1]+sum[i]就行了。

    但是你会发现求w[x][j]的方法对某些值已经等于j的点不公平,他们多加了一遍m*b[i],于是对于每个点,w[x][a[i]]-=m*b[i]。

    于是我们有了优秀的求w数组的方法了,可以通过本题。

    #include<cmath>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=1e7+5;
    const int RN=5010;
    const int M=205;
    const ll INF=1e18;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    unsigned int SA,SB,SC;
    int n,m,k,p,A,B,a[N],b[N],lim,d[RN];
    ll w[RN][M],sum[RN],f[RN][M];
    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(){
        n=read(),k=read(),m=read(),p=read();
        SA=read(),SB=read(),SC=read(),A=read(),B=read();
        for(int i=1;i<=p;i++)a[i]=read(),b[i]=read();
        for(int i=p+1;i<=n;i++){
            a[i]=rng61()%A+1;
            b[i]=rng61()%B+1;
        }
    }
    void dfs(int x){
        int l=x<<1,r=x<<1|1;
        if(l>lim){
        for(int i=0;i<m;i++)f[x][i]=w[x][i];
        return;
        }
        for(int i=0;i<m;i++)f[x][i]=INF;
        dfs(l);
        if(r>lim||d[l]!=d[r]){
        for(int i=0;i<m;i++)
            for(int j=0;j<m;j++)
            f[x][(i+j)%m]=min(f[x][(i+j)%m],w[x][i]+f[l][j]);
        return;
        }
        dfs(r);
        for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            f[x][(i+j)%m]=min(f[x][(i+j)%m],w[x][i]+f[l][j]+f[r][j]);
    }
    inline void init(){
        memset(sum,0,sizeof(sum));
        memset(w,0,sizeof(w));
    }
    int main(){
        int T=read();
        while(T--){
        init();gen();k++;
        lim=min((1<<k)-1,n);
        for(int i=1,l=0;i<=n;i++){
            int j=i;
            while((j>>l)>lim)l+=k;
            j>>=l;
            a[i]%=m;
            sum[j]+=b[i];
            w[j][0]+=(m-a[i])*b[i];
            w[j][a[i]]-=m*b[i];
        }
        for(int i=lim;i;i--){
            d[i]=0;
            if((i<<1)<=lim)d[i]=d[i<<1];
            d[i]++;
        }
        for(int i=1;i<=lim;i++){
            for(int j=1;j<m;j++)w[i][j]+=w[i][j-1]+sum[i];
        }
        dfs(1);
        printf("%lld
    ",f[1][0]);
        }
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

    +本文作者:luyouqi233。               +

    +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    剑指前端(前端入门笔记系列)——BOM
    剑指前端(前端入门笔记系列)——DOM(元素大小)
    剑指前端(前端入门笔记系列)——DOM(属性节点)
    剑指前端(前端入门笔记系列)——DOM(元素节点)
    剑指前端(前端入门笔记系列)——Math对象
    剑指前端(前端入门笔记系列)——DOM(基本组成与操作)
    剑指前端(前端入门笔记系列)——数组(方法)
    剑指前端(前端入门笔记系列)——数组(基本语法)
    剑指前端(前端入门笔记系列)——Date对象
    根据不同域名实现数据源切换
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/9077548.html
Copyright © 2020-2023  润新知