• 【HNOI 2017】大佬


    Problem

    Description

    人们总是难免会碰到大佬。他们趾高气昂地谈论凡人不能理解的算法和数据结构,走到任何一个地方,大佬的气场就能让周围的人吓得瑟瑟发抖,不敢言语。你作为一个 OIer,面对这样的事情非常不开心,于是发表了对大佬不敬的言论。 大佬便对你开始了报复,你也不示弱,扬言要打倒大佬。现在给你讲解一下什么是大佬,大佬除了是神犇以外,还有着强大的自信心,自信程度可以被量化为一个正整数 (C),想要打倒一个大佬的唯一方法是摧毁 Ta 的自信心,也就是让大佬的自信值等于 (0)(恰好等于 (0),不能小于 (0))。由于你被大佬盯上了,所以你需要准备好 (n) 天来和大佬较量,因为这 (n) 天大佬只会嘲讽你动摇你的自信,到了第 (n+1) 天,如果大佬发现你还不服,就会直接虐到你服,这样你就丧失斗争的能力了。

    你的自信程度同样也可以被量化,我们用 (mathrm{mc}) 来表示你的自信值上限。在第 (i (ige 1)) 天,大佬会对你发动一次嘲讽,使你的自信值减小 (a_i),如果这个时刻你的自信值小于 (0) 了,那么你就丧失斗争能力,也就失败了(特别注意你的自信值为 (0) 的时候还可以继续和大佬斗争)。 在这一天,大佬对你发动嘲讽之后,如果你的自信值仍大于等于 (0),你能且仅能选择如下的行为之一

    1. 还一句嘴,大佬会有点惊讶,导致大佬的自信值 (C) 减小 (1)

    2. 做一天的水题,使得自己的当前自信值增加 (w_i),并将新自信值和自信值上限 (mathrm{mc}) 比较,若新自信值大于 (mathrm{mc}),则新自信值更新为 (mathrm{mc})。例如,(mathrm{mc} = 50),当前自信值为 (40),若 (w_i = 5),则新自信值为 (45),若 (w_i = 11),则新自信值为 (50)

    3. 让自己的等级值 (L)(1)

    4. 让自己的讽刺能力 (F) 乘以自己当前等级 (L),使讽刺能力 (F) 更新为 (Fcdot L)

    5. 怼大佬,让大佬的自信值 (C) 减小 (F)。并在怼完大佬之后,你自己的等级 (L) 自动降为 (0),讽刺能力 (F) 降为 (1)。由于怼大佬比较掉人品,所以这个操作只能做不超过两次

    特别注意的是,在任何时候,你不能让大佬的自信值为负,因为自信值为负,对大佬来说意味着屈辱,而大佬但凡遇到屈辱就会进化为更厉害的大佬直接虐飞你。在第 (1) 天,在你被攻击之前,你的自信是满的(初始自信值等于自信值上限 (mathrm{mc})),你的讽刺能力 (F)(1),等级是 (0)

    现在由于你得罪了大佬,你需要准备和大佬正面杠,你知道世界上一共有 (m) 个大佬,他们的嘲讽时间都是 (n) 天,而且第 (i) 天的嘲讽值都是 (a_i)。不管和哪个大佬较量,你在第 (i) 天做水题的自信回涨都是 (w_i)。这 (m) 个大佬中只会有一个来和你较量((n) 天里都是这个大佬和你较量),但是作为你,你需要知道对于任意一个大佬,你是否能摧毁他的自信,也就是让他的自信值恰好等于 (0)。和某一个大佬较量时,其他大佬不会插手。

    Input Format

    第一行三个正整数 (n,m,mathrm{mc})。分别表示有 (n) 天和 (m) 个大佬,你的自信上限为 (mathrm{mc})

    接下来一行是用空格隔开的 (n) 个数,其中第 (i(1le ile n)) 个表示 (a_i)

    接下来一行是用空格隔开的 (n) 个数,其中第 (i(1le ile n)) 个表示 (w_i)

    接下来 (m) 行,每行一个正整数,其中第 (k(1le kle m)) 行的正整数 (C_k) 表示第 (k) 个大佬的初始自信值。

    Output Format

    (m) 行,如果能战胜第 (k) 个大佬(让他的自信值恰好等于 0),那么第 (k) 行输出 (1),否则输出 (0)

    Sample

    Input

    30 20 30
    15 5 24 14 13 4 14 21 3 16 7 4 7 8 13 19 16 5 6 13 21 12 7 9 4 15 20 4 13 12
    22 21 15 16 17 1 21 19 11 8 3 28 7 10 19 3 27 17 28 3 26 4 22 28 15 5 26 9 5 26
    30
    10
    18
    29
    18
    29
    3
    12
    28
    11
    28
    6
    1
    6
    27
    27
    18
    11
    26
    1
    

    Output

    0
    1
    1
    0
    1
    0
    1
    1
    0
    0
    0
    1
    1
    1
    1
    1
    1
    0
    0
    1
    

    Range

    对于 (20\%) 的数据,(1le nle 10)

    另有 (20\%) 数据,(1le C_i,n,mathrm{mc}le 30)

    对于 (100\%) 的数据,(1le n, mathrm{mc}le 100, 1le mle 20; 1le a_i, w_ilemathrm{mc}, 1le C_ile 10^8)

    Algorithm

    (DP),广搜

    Mentality

    我感觉我做了一道搜索题 (......) 蓝瘦。而且题面也太过真实了。

    我们第一个瞬间就该发现,每个大佬只有血量不同,那么我们为了打败大佬,肯定要用尽量多的时间来降低他的自信,则由于其他量都相等,我们完全可以先求出最多能用多少天来与大佬战♂斗对抗。

    这个很简单,来一发 (DP) 就好,设 (f[i][j]) 为到了第 (i) 天剩余 (j) 点自信时,最多能花多少天嘲讽大佬。然后取 (n^2) 数组内的最大值 (D) 作为最大天数。

    然后我们要统计如何分配怼大佬的那两次。

    首先,如果大佬的自信值小于等于 (D) ,我们直接不停还嘴就行。

    那么我们讨论一下怼大佬的情况。

    先计算出我们的嘲讽能力为 (F) 时所需花费的最小天数 (D) ,求法后面再讲,它太过暴力了 (emm...)

    我们考虑枚举两次怼大佬时的嘲讽能力 (F1,F2) ,显然在相同 (F) 值的情况下花在准备上的时间要越少越好,我们设两者的时间为 (D1,D2)

    那么我们这两次怼大佬必定要满足两个条件:不能把大佬怼死了;剩下的时间通过还嘴可以刚好打败大佬。

    转化成不等式如下:

    [F1+F2le C ,F1+F2+(D-D1-D2)ge C ]

    那我们只需要枚举一个 (F1) ,在这种情况下,(F1,D1) 都已经固定了,我们可以找到一个满足第一个不等式的,具有最优性的 (F2) ,也即 (F2-D2) 在满足第一个不等式的条件下最小,那么这时我们计算 (F1+F2+(D-D1-D2)) 是否大于等于 (C) ,如果大于则此次询问答案为 (YES) ,如果枚举遍所有的 (F1) 都无法满足第二个不等式那答案就为 (NO)

    所以我们可以把所有计算出来的状态按 (F) 排序,然后从大到小枚举 (F1) ,而 (F2) 的寻找范围也是单调递增的,那么我们扫 (F2) 的指针沿用上次的即可。

    那么重点来了,怎么计算状态呢?答案是 -- 广搜 (......)

    我们只需要带着三个数值所代表的状态 (dfs) 即可,分别是 (step) -- 使用 (step) 天,(L) -- 当前等级,(F) -- 当前嘲讽能力。然后直接广搜肯定是不行的,我们来观察一下:由于在相同的 (F) 下,所花天数越小越好。而由于我们使用的是广搜,所以当我们搜索到一个新状态 (step,L,F) 时,相对于当前的 (L,F)(step) 就肯定是最优天数了。所以我们只需要使用哈希表判重,如果后面再搜到相同的 (L,F) ,那就不加入搜索队列了。

    暴力否?

    如若不懂详见代码。

    为了卡常长得蛮奇怪。

    Code

    #include <algorithm>
    #include <cstdio>
    #include <iostream>
    #include <queue>
    using namespace std;
    #define f(i) STA[i].first
    #define d(i) STA[i].second
    const int mod = 9e5 + 1;
    int n, m, limit, maxx, cnt, C, a[101], w[101], c[21];
    int f[101][101], D;
    inline void read(int &x) {
      x = 0;
      char ch = getchar();
      while (!isdigit(ch)) ch = getchar();
      while (isdigit(ch)) {
        x = x * 10 + ch - '0';
        ch = getchar();
      }
    }
    pair<int, int> STA[1000001];
    struct node {
      int L, F, t;
    };
    struct Check {
      int x[mod], y[mod], nx[mod], head[mod], cnt;
      int get_key(int X, int Y) { return (998244ll * X + Y) % mod; }
      inline void add(int X, int Y) {
        int now = get_key(X, Y);
        cnt++;
        nx[cnt] = head[now], x[cnt] = X, y[cnt] = Y;
        head[now] = cnt;
      }
      inline bool query(int X, int Y) {
        int now = get_key(X, Y);
        for (register int i = head[now]; i; i = nx[i])
          if (X == x[i] && Y == y[i]) return true;
        return false;
      }
    } Map, M;
    inline void Max(int &a, int b) { a = a < b ? b : a; }
    queue<node> q;
    inline void bfs() {
      q.push((node){0, 1, 1});
      while (!q.empty()) {
        node now = q.front();
        q.pop();
        if (now.t == D) continue;
        q.push((node){now.L + 1, now.F, now.t + 1});
        if (now.L > 1 && 1ll * now.L * now.F <= maxx &&
            !Map.query(now.L * now.F, now.L))  //手写 Map 判重
        {
          int A = now.L * now.F, B = now.t + 1;
          q.push((node){now.L, A, B});
          if (!M.query(A, 9181283))
            STA[++cnt] = make_pair(A, B),
            M.add(A, 9181283);  //相同 F 下所花天数越少越好
          Map.add(A, now.L);
        }
      }
    }
    int main() {
      read(n), read(m), read(limit);
      for (register int i = 1; i <= n; i++) read(a[i]);
      for (register int i = 1; i <= n; i++) read(w[i]);
      for (register int i = 1; i <= m; i++)
        read(c[i]), Max(maxx, c[i]);  //先处理出搜索的上限 -- F
                                      //值至少不能大于自信值最强的大佬的自信吧
      for (register int i = 1; i <= n; i++)
        for (register int j = a[i]; j <= limit; j++)
          Max(f[i][j - a[i]], f[i - 1][j] + 1),
              Max(f[i][min(limit, j - a[i] + w[i])],
                  f[i - 1][j]);  //先 DP 出最大天数
      for (register int i = 1; i <= n; i++)
        for (register int j = 1; j <= limit; j++) Max(D, f[i][j]);  //取 max
      bfs();                                                        //开始广搜
      sort(STA + 1, STA + cnt + 1);
      for (register int i = 1; i <= m; i++) {
        if (c[i] <= D) {
          printf("1
    ");
          continue;
        }
        int mmax = -1e9, Ans = 0;
        for (register int l = 1, r = cnt; r; r--) {
          while (f(l) + f(r) <= c[i] && l < cnt)
            Max(mmax, f(l) - d(l)), l++;                //移动
          if (f(r) + D - d(r) + mmax >= c[i]) Ans = 1;  //是否满足第二个不等式
          if (f(r) <= c[i] && f(r) + D - d(r) >= c[i]) Ans = 1;
        }
        printf("%d
    ", Ans);
      }
    }
    
    
  • 相关阅读:
    delphi private public protected
    delphi 程序流程控制
    TTrayIcon-Delphi系统托盘组件
    如果没有你-莫文蔚
    ShellExecute 调用bat文件
    delphi ShellExecute使用
    delphi 测试ping
    centos7 安装redis
    my.cnf 基础配置
    Delphi的类和对象(七)- 继承
  • 原文地址:https://www.cnblogs.com/luoshuitianyi/p/10679302.html
Copyright © 2020-2023  润新知