zoukankan      html  css  js  c++  java
  • 【BZOJ 2138】stone

    Problem

    Description

    话说 (Nan) 在海边等人,预计还要等上 (M) 分钟。为了打发时间,他玩起了石子。

    (Nan) 搬来了 (N) 堆石子,编号为 (1)(N),每堆包含 (A_i) 颗石子。

    (1) 分钟,(Nan) 会在编号在 ([L_i, R_i]) 之间的石堆中挑出任意 (K_i) 颗扔向大海(好疼的玩法),如果 ([L_i, R_i]) 剩下石子不够 (K_i) 颗,则取尽量地多。为了保留扔石子的新鲜感,(Nan) 保证任意两个区间 ([L_i, R_i])([L_j, R_j]) ,不会存在 (L_ile L_j & R_jle R_i) 的情况,即任意两段区间不存在包含关系。

    可是,如果选择不当,可能无法扔出最多的石子,这时 (Nan) 就会不高兴了。所以他希望制定一个计划,他告诉你他 (m) 分钟打算扔的区间 ([L_i, R_i]) 以及 (K_i)

    现在他想你告诉他,在满足前 (i-1) 分钟都取到你回答的颗数的情况下,第 (i) 分钟最多能取多少个石子。

    Input Format

    第一行正整数 (N),表示石子的堆数;

    第二行正整数 (x,y,z,P),((1le x,y,zle N, Ple500))

    有等式 (A_i=[(i-x)^2+(i-y)^2+(i-z)^2] mod P)

    第三行正整数 (M) ,表示有 (M) 分钟;

    第四行正整数 (K_1,K_2,x,y,z,P),((x,y,zle1000,Ple10000))

    有等式 (K_i=(x*K_{i-1}+y*K_{i-2}+z)mod P)

    接下来 (M) 行,每行两个正整数 (L_i,R_i)

    (Nle40000, Mle N, 1le L_ile R_ile N, A_ile500)

    Output Format

    (M) 行,第 (i) 行表示第 (i) 分钟最多能取多少石子。

    Sample

    Input

    5
    3 2 4 7
    3
    2 5 2 6 4 9
    2 4
    1 2
    3 5
    

    Output

    2
    5
    5
    

    Explanation

    石子每堆个数分别为 (0,5,2,5,0)

    (1) 分钟,从第 (2) 到第 (4) 堆中选 (2) 个;

    (2) 分钟,从第 (1) 到第 (2) 堆中选 (5) 个;

    (3) 分钟,从第 (3) 到第 (5) 堆中选 (8) 个,但最多只能选 (5) 个。

    Algorithm

    线段树

    Mentality

    神奇题目。由于它要求的策略不针对整体,只需要局部最优,所以才有了解法。

    设当前处理到了第 (p) 个区间。

    (S_{i,j}) 为区间 ([i,j]) 里的石子数之和,设 (T_{i,j}) 为之前的,严格为 ([i,j]) 子区间的询问区间取的石子数之和。

    则在任一时刻,对于任意区间 ([l,r]) 都应该有 (T_{l,r}le S_{l,r}) ,毕竟拿的石子数总不能多于存在的。

    (s_i)([1,i]) 的石子数和,(Tl_i) 为之前的,左端点位于 ([1,i]) 的询问区间取的石子数之和,(Tr_i) 为之前的,右端点端点位于 ([1,i]) 的询问区间取的石子数之和。

    则可以用它们写成不等式:

    [T_{l,r}le S_{l,r}\ Tr_r-Tl_{l-1}le s_r-s_{l-1}\ s_{l-1}-Tl_{l-1}le s_r-Tr_r ]

    (g_i = s_{i - 1} - Tl_{i - 1}, f_i = s_i - Tr_i) ,则必须有 (g_lle f_r) 。对于一个区间而言,最多能取 (S_{l,r} - T_{l,r}=f_r-g_l) 个石子。

    考虑对于当前询问 (p) 而言,([L_p,R_p]) 的决策会影响到所有包含此区间的区间 (T) 值。为了满足 (forall T_{l,r}le S_{l,r}) ,则我们能选择的,最多的石子数应该是 (Min_{lin[1,L_p], rin [R_p, n]} S_{l,r} - T_{l,r}) ,然后和询问所需石子数 (K_p)(Min)

    由于 (S_{l,r} - T_{l,r}) 可以写成 (f_r-g_l) 的形式,所以每个询问的答案就可以写成 (Min_{iin[R_p,n]}f_i - Max_{iin[1,L_p]}g_i)

    用线段树维护即可。

    Code

    #include <algorithm>
    #include <cmath>
    #include <complex>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <map>
    #include <queue>
    #include <set>
    #include <vector>
    using namespace std;
    long long read() {
      long long x = 0, w = 1;
      char ch = getchar();
      while (!isdigit(ch)) w = ch == '-' ? -1 : 1, ch = getchar();
      while (isdigit(ch)) {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
      }
      return x * w;
    }
    #define ls (o << 1)
    #define rs ((o << 1) + 1)
    #define mid ((l + r) >> 1)
    const int Max_n = 4e4 + 5;
    int n, m, x, y, z, mod, Ans;
    int s[Max_n];
    int f[Max_n << 2], g[Max_n << 2], tagf[Max_n << 2], tagg[Max_n << 2];
    int L, R, ans;
    int ned[Max_n], ln, rn;
    bool fl;
    int sqr(int x) { return x * x; }
    void pushup(int *f, int o) {
      if (!fl)
        f[o] = min(f[ls], f[rs]);
      else
        f[o] = max(f[ls], f[rs]);
    }
    void pushdown(int *f, int *t, int o) {
      t[ls] += t[o], f[ls] += t[o];
      t[rs] += t[o], f[rs] += t[o];
      t[o] = 0;
    }
    void build(int *f, int o, int l, int r) {
      if (l == r) {
        if (!fl)
          f[o] = s[l];
        else
          if (l) f[o] = s[l - 1];
        return;
      }
      build(f, ls, l, mid);
      build(f, rs, mid + 1, r);
      pushup(f, o);
    }
    void query(int *f, int *t, int o, int l, int r) {
      if (L > R) return;
      if (l >= L && r <= R) {
        if (!fl)
          ans = min(ans, f[o]);
        else
          ans = max(ans, f[o]);
        return;
      }
      pushdown(f, t, o);
      if (mid >= L) query(f, t, ls, l, mid);
      if (mid < R) query(f, t, rs, mid + 1, r);
      pushup(f, o);
    }
    void add(int *f, int *t, int o, int l, int r) {
      if (L > R) return;
      if (l >= L && r <= R) {
        f[o] += ans, t[o] += ans;
        return;
      }
      pushdown(f, t, o);
      if (mid >= L) add(f, t, ls, l, mid);
      if (mid < R) add(f, t, rs, mid + 1, r);
      pushup(f, o);
    }
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("2138.in", "r", stdin);
      freopen("2138.out", "w", stdout);
    #endif
      n = read(), x = read(), y = read(), z = read(), mod = read();
      for (int i = 1; i <= n; i++) {
        s[i] = (sqr(x - i) % mod + sqr(y - i) % mod + sqr(z - i) % mod) % mod;
        s[i] += s[i - 1];
      }
      fl = 0, build(f, 1, 0, n);
      fl = 1, build(g, 1, 0, n);
      m = read();
      ned[1] = read(), ned[2] = read();
      x = read(), y = read(), z = read(), mod = read();
      for (int i = 3; i <= m; i++)
        ned[i] = (x * ned[i - 1] % mod + y * ned[i - 2] % mod + z) % mod;
      for (int q = 1; q <= m; q++) {
        ln = read(), rn = read();
        ans = -2e9, fl = 1, L = 1, R = ln;
        query(g, tagg, 1, 0, n);
        Ans = -ans, ans = 2e9, fl = 0, L = rn, R = n;
        query(f, tagf, 1, 0, n);
        Ans = min(Ans + ans, ned[q]);
        printf("%d
    ", Ans);
        fl = 1, L = ln + 1, ans = -Ans;
        add(g, tagg, 1, 0, n);
        fl = 0, L = rn;
        add(f, tagf, 1, 0, n);
      }
    }
    
  • 相关阅读:
    hdu6229 Wandering Robots 2017沈阳区域赛M题 思维加map
    hdu6223 Infinite Fraction Path 2017沈阳区域赛G题 bfs加剪枝(好题)
    hdu6438 Buy and Resell 买卖物品 ccpc网络赛 贪心
    hdu6441 Find Integer 求勾股数 费马大定理
    bzoj 1176 Mokia
    luogu 3415 祭坛
    bzoj 1010 玩具装箱
    bzoj 3312 No Change
    luogu 3383【模板】线性筛素数
    bzoj 1067 降雨量
  • 原文地址:https://www.cnblogs.com/luoshuitianyi/p/11443382.html
Copyright © 2011-2022 走看看