• [loj#2566][BZOJ5333] [Sdoi2018]荣誉称号 树形dp


    #2566. 「SDOI2018」荣誉称号

     

    休闲游戏玩家小 Q 不仅在算法竞赛方面取得了优异的成绩,还在一款收集钻石的游戏中排名很高。 这款游戏一共有 n 种不同类别的钻石,编号依次为 1 到 n。小 Q 已经玩了这款游戏很久了,对于第 i 种钻石,他已经收集到了 ai 个。这款游戏最大的亮点就是,钻石只有一种获得途径,那就是从商城中 购买。具体来说,第 i 种钻石的单价为 bi 点券。为了鼓励玩家充值,每种钻石都没有数量上限,只要肯 充钱,就可以拥有任意多的钻石。但是这款游戏并没有开发 “丢弃道具” 功能,因此小 Q 不能通过丢弃 钻石去完成任务。 最近这款游戏推出了一个限时成就任务,完成任务的玩家可以获得荣誉称号,而完成任务条件则是: 给定正整数 k 和 m,对于任意一个整数 x(2k ≤ x ≤ n),ax + a⌊ x 2 ⌋ + a⌊ x 4 ⌋ + a⌊ x 8 ⌋ + ... + a⌊ x 2k ⌋ 都要是 m 的倍数。 高玩小 Q 当然想完成这个限时成就任务,但是在充钱之前他想知道他究竟需要多少点券才能完成这 个任务。请写一个程序帮助小 Q 计算最少需要的点券数量。

    Input 第一行包含一个正整数 T,表示测试数据的组数。 每组数据第一行包含 9 个正整数 n, k, m, p, SA, SB, SC, A, B,其中 n 表示钻石种类数,k, m 表示任 务条件。 为了在某种程度上减少输入量,a[] 和 b[] 由以下代码生成: 

    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("%d%d", &a[i], &b[i]);
        for(int i = p + 1; i <= n; i++){
            a[i] = rng61() % A + 1;
            b[i] = rng61() % B + 1;
        }
    }
    

    如对数据的生成方式仍有疑问,请参考下发文件中的参考程序。

    输出格式

    对于每组数据,输出一行一个整数,即最少需要的点券数量。

    样例

    输入样例 1

    2
    3 1 2 3 11111 22222 33333 1 1
    1 5
    2 3
    3 6
    7 2 3 7 11111 22222 33333 1 1
    6 9
    4 5
    3 7
    5 2
    2 4
    1 7
    9 6

    输出样例 1

    3
    14

    样例 2

    见下发文件。

    数据范围与提示

    • 1 ≤ T ≤ 10,

    • 1 ≤ k ≤ 10 且 2 k ≤ n,

    • 1 ≤ p ≤ min(n, 100000),10000 ≤ SA, SB, SC ≤ 1000000,

    • 1 ≤ A, B, ai , bi ≤ 107。

    子任务 1(30 分):满足 1 ≤ n ≤ 1000 且 m = 2。

    子任务 2(40 分):满足 1 ≤ n ≤ 10^5 且 m ≤ 200。

    子任务 3(30 分):满足 1 ≤ n ≤ 10^7 且 m ≤ 200。

    首先我们发现他是一颗完全二叉树。

    对于一条深度为i的点,我们发现深度为i+k的点与他%m同余一个节点的两个叶子也同余。

    于是我们可以将标号大于2^k的点去除,将他的贡献加到编号为1-2^k的点上。

    我们处理出编号为1-2^k的点中将他%m的值改为j的最小代价g[i][j]

    显然g可以用dp求出。

    之后我们在大小为2^k的树上做树形dp。

    f[i][j]表示节点i到叶子的和%m余j的最小代价。

    答案为f[1][0]

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<cmath>
     5 #include<algorithm>
     6 #include<cstdio>
     7 #define maxn 10000005
     8 #define ll long long
     9 using namespace std;
    10 inline int read() {
    11     int x=0,f=1;char ch=getchar();
    12     for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    13     for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    14     return x*f;
    15 }
    16 int T;int n,k,m;
    17 unsigned int SA, SB, SC;int p, A, B;
    18 int a[maxn],b[maxn];
    19 unsigned int rng61(){
    20     SA ^= SA << 16;SA ^= SA >> 5;SA ^= SA << 1;
    21     unsigned int t = SA;
    22     SA = SB;SB = SC;SC ^= t ^ SA;
    23     return SC;
    24 }
    25 void gen(){
    26     n=read(),k=read(),m=read(),p=read();
    27     scanf("%u%u%u%d%d",&SA, &SB, &SC, &A, &B);
    28     for(int i = 1; i <= p; i++)scanf("%d%d", &a[i], &b[i]);
    29     for(int i = p + 1; i <= n; i++){
    30         a[i] = rng61() % A + 1;
    31         b[i] = rng61() % B + 1;
    32     }
    33 }
    34 ll g[5555][205],f[5555][205],sum[5555];
    35 int lim;
    36 void init() {
    37     memset(g,0,sizeof(g));
    38     memset(sum,0,sizeof(sum));
    39     memset(f,0,sizeof(f));
    40 }
    41 int main() {
    42     T=read();
    43     while(T--) {
    44         gen();init();k++;
    45         lim=(1<<k)-1;
    46         int l=0;
    47         for(int i=1;i<=n;i++) {
    48             int j=i;
    49             while((j>>l)>lim) l+=k;
    50             j>>=l;
    51             a[i]%=m;sum[j]+=b[i];
    52             g[j][0]+=(m-a[i])*b[i];
    53             g[j][a[i]]-=m*b[i];
    54         }
    55         for(int i=1;i<=lim;i++)
    56             for(int j=1;j<m;j++) g[i][j]+=g[i][j-1]+sum[i];
    57         for(int i=lim;i>=1;i--) {
    58             if(i*2>lim) {
    59                 for(int j=0;j<m;j++) f[i][j]=g[i][j];
    60                 continue;
    61             }
    62             for(int j=0;j<m;j++) {
    63                 f[i][j]=214748364700000000ll;
    64                 for(int k=0;k<m;k++) {
    65                     int tt=j-k;tt=tt<0?tt+m:tt;
    66                     f[i][j]=min(f[i][j],f[i<<1][tt]+f[(i<<1)+1][tt]+g[i][k]);
    67                 }
    68             }
    69         }
    70         printf("%lld
    ",f[1][0]);
    71     }
    72 }
    73 
    View Code
  • 相关阅读:
    Spring AOP前置通知实例说明AOP相关概念
    什么是面向切面编程AOP
    关于IOC容器的一些个人理解
    在.Net Core WebAPI下给Swagger增加导出离线文档功能
    .Net Core ORM选择之路,哪个才适合你
    真香.小程序云开发(时光邮局小程序)
    Cordova的安装与配置
    JS三座大山再学习(三、异步和单线程)
    JS三座大山再学习(二、作用域和闭包)
    JS三座大山再学习(一、原型和原型链)
  • 原文地址:https://www.cnblogs.com/wls001/p/9852828.html
Copyright © 2020-2023  润新知