@atcoder - [email protected] Contest with Drinks Hard

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定序列 T1, T2, ... TN,你可以从中选择一些 Ti,可以选择 0 个(即不选)。

定义你选择的权值 = (满足 T[L...R] 都被选择的区间 [L, R] 的数量)-(你选择的 Ti 之和),你希望这个权值尽量大。

现在有 M 次询问,每次询问假如将 T[Pi] 修改成 Xi,你所能选出的最大权值。

Constraints
1 <= N, M <= 3*10^5,1 <= Ti <= 10^9 且所有 Ti 总和 <= 10^12。
对于每组询问,满足 1 <= Pi <= N,1 <= Xi <= 10^9。

Input
输入形式如下:
N
T1 T2 … TN
M
P1 X1
P2 X2
:
PM XM

Output
对于每组询问,输出其答案。

Sample Input 1
5
1 1 4 1 1
2
3 2
3 10
Sample Output 1
9
2

@[email protected]

考虑在修改 T[Pi] 为 Xi 后,最后的选择方案要么包含 Pi 要么不包含。
假如不包含,则只需要在 1 ... Pi - 1 与 Pi + 1 ... N 中选择得到最大值。
假如包含,我们需要预处理包含 Pi 的最优解,然后用这个最优解 + T[Pi] - Xi。

综上,我们需要处理出三个东西:第一个 f[i] 表示从 1 ... i 中选择的最优答案,第二个 g[i] 表示从 i ... N 中选择的最优答案,第三个 h[i] 表示必须选择 i 的最优答案。

先一步步考虑,考虑求 f[i]。要么不选 i 从 f[i-1] 转移过来;要么选择 i,我们枚举一个 j 表示我们强制选择 i + 1 ... j 中的所有数,则贡献为 f[j] + ([i + 1, j] 中的区间数量)-([i + 1, j] 的 T 值之和)。
通过预处理前缀和 S[i] = T[1] + T[2] + ... T[i],可以将转移式写得更具体一点:
\[f[i] = \max(f[i-1], \max_{0\le j<i}(f[j] + S[j] - S[i] + \frac{(i-j+1)*(i-j)}{2}))\]

上面那个转移非常斜率优化,稍微变化一下式子发现的确可以斜率优化。这里就不具体写了。
总之,这个式子是个斜率单增且横坐标单增的斜率优化,可以使用单调栈做到 O(n) 的时间复杂度。
而 g[i] 可以类似地处理。这里也不具体谈了。

现在考虑怎么求 h[i]。最暴力的方法是在 i 左边枚举一个左端点 l,i 右边枚举一个右端点 r,算出区间 [l, r] 的贡献再加上 f[l - 1] + g[r + 1]。
考虑优化,我们发现 cdq 分治非常合适(其实我也不知道它该不该叫作cdq分治,只是感觉比较像)

对于区间 [left, right],设它的中点为 mid,我们仅考虑满足 left <= l <= mid < r <= right 的区间 [l, r] 对 h[l...r] 的贡献。
然后递归到 [left, mid] 与 [mid + 1, right],继续处理子问题,直到 left = right。

怎么处理呢?我们先将 [left, mid] 的点加入单调栈;然后从左往右扫 [mid + 1, right] 用斜率优化求出 [mid + 1, right] 内所有点作为右端点的答案;然后再从右往左扫描,扫到 x 时维护出 [x, right] 的最大值 tmp,用这个 tmp 更新 h[x]。
再反过来将 [mid + 1, right] 加入单调栈,更新 [left, mid] 的 h 值。这个过程与上面类似,不再赘述。

@accepted [email protected]

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 300000;
const ll INF = (1LL<<60);
ll f[MAXN + 5], g[MAXN + 5], h[MAXN + 5];
ll S[MAXN + 5], T[MAXN + 5];
ll fk(int i) {return i;}
ll fx(int j) {return 2LL*j;}
ll fc(int i) {return 1LL*i*i + i - 2*S[i];}
ll fy(int j) {return 2*S[j] + f[j] - j + 1LL*j*j;}
double fslope(int p, int q) {return 1.0*(fy(q) - fy(p)) / (fx(q) - fx(p));}
ll gk(int i) {return -i;}
ll gx(int j) {return -2LL*j;}
ll gc(int i) {return 1LL*i*i - i + 2*S[i-1];}
ll gy(int j) {return -2*S[j-1] + g[j] + j + 1LL*j*j;}
double gslope(int p, int q) {return 1.0*(gy(q) - gy(p)) / (gx(q) - gx(p));}
int stk[MAXN + 5], tp;
int N, M;
void get_dp() {
    f[0] = 0; stk[tp = 1] = 0;
    for(int i=1;i<=N;i++) {
        while( tp > 1 && fk(i) >= fslope(stk[tp], stk[tp-1]) )
            tp--;
        f[i] = max(f[i - 1], fc(i) - fk(i)*fx(stk[tp]) + fy(stk[tp]));
        while( tp > 1 && fslope(i, stk[tp]) >= fslope(stk[tp], stk[tp-1]) )
            tp--;
        stk[++tp] = i;
    }
    g[N + 1] = 0; stk[tp = 1] = N + 1;
    for(int i=N;i>=1;i--) {
        while( tp > 1 && gk(i) >= gslope(stk[tp], stk[tp-1]) )
            tp--;
        g[i] = max(g[i + 1], gc(i) - gk(i)*gx(stk[tp]) + gy(stk[tp]));
        while( tp > 1 && gslope(i, stk[tp]) >= gslope(stk[tp], stk[tp-1]) )
            tp--;
        stk[++tp] = i;
    }
}
ll tmp[MAXN + 5];
void divide_conquer(int l, int r) {
    if( l == r ) {
        h[l] = f[l + 1] - 2*T[l] + g[r + 1] + 2;
        return ;
    }
    int mid = (l + r) >> 1;
    divide_conquer(l, mid);
    divide_conquer(mid + 1, r);
    stk[tp = 1] = l - 1;
    for(int i=l;i<mid;i++) {
        while( tp > 1 && fk(i) >= fslope(stk[tp], stk[tp-1]) )
            tp--;
        while( tp > 1 && fslope(i, stk[tp]) >= fslope(stk[tp], stk[tp-1]) )
            tp--;
        stk[++tp] = i;
    }
    for(int i=mid+1;i<=r;i++) {
        while( tp > 1 && fk(i) >= fslope(stk[tp], stk[tp-1]) )
            tp--;
        tmp[i] = fc(i) - fk(i)*fx(stk[tp]) + fy(stk[tp]) + g[i + 1];
    }
    ll p = -INF;
    for(int i=r;i>mid;i--) {
        p = max(p, tmp[i]);
        h[i] = max(h[i], p);
    }
    stk[tp = 1] = r + 1;
    for(int i=r;i>mid+1;i--) {
        while( tp > 1 && gk(i) >= gslope(stk[tp], stk[tp-1]) )
            tp--;
        while( tp > 1 && gslope(i, stk[tp]) >= gslope(stk[tp], stk[tp-1]) )
            tp--;
        stk[++tp] = i;
    }
    for(int i=mid;i>=l;i--) {
        while( tp > 1 && gk(i) >= gslope(stk[tp], stk[tp-1]) )
            tp--;
        tmp[i] = gc(i) - gk(i)*gx(stk[tp]) + gy(stk[tp]) + f[i - 1];
    }
    p = -INF;
    for(int i=l;i<=mid;i++) {
        p = max(p, tmp[i]);
        h[i] = max(h[i], p);
    }
}
int main() {
    scanf("%d", &N);
    for(int i=1;i<=N;i++)
        scanf("%lld", &T[i]), S[i] = S[i - 1] + T[i];
    get_dp(), divide_conquer(1, N);
    scanf("%d", &M);
    for(int i=1;i<=M;i++) {
        int P, X; scanf("%d%d", &P, &X);
        printf("%lld\n", max(f[P-1] + g[P+1], h[P] + 2*T[P] - 2*X)/2);
    }
}

@[email protected]

h[i] 值是必须要包含 i 的,所以可能为负数,给 h 初始化时应该赋值为 -inf。

话说现在 atcoder 都不办 ARC 了,ARC 已经成为了时代的眼泪。。。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11407452.html

时间: 2024-08-30 12:40:25

@atcoder - [email protected] Contest with Drinks Hard的相关文章

@atcoder - [email&#160;protected] Colorful Tree

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 题面明天起来补... @[email protected] 跟颜色有关,虽然题目中说是要"修改"实际上每一个询问之间是独立的,即修改不会有时效性. 所以不难想到可以使用树上莫队. 首先将边的颜色与边权下放至点.然后莫队时,统计出区间内每个点的点权(就是下放的边权)和 S.区间

@atcoder - [email&#160;protected] Gachapon

目录 @[email protected] @solution - [email protected] @accepted code - [email protected] @solution - [email protected] @accepted code - [email protected] @[email protected] @[email protected] 一个随机数发生器会生成 N 种数. 第 i 种数有参数 Ai,记 SA = ∑Ai,随机数发生器会有 Ai/SA 的概率

@atcoder - [email&#160;protected] RNG and XOR

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个值域在 [0, 2^N) 的随机数生成器,给定参数 A[0...2^N-1]. 该生成器有 \(\frac{A_i}{\sum A}\) 的概率生成 i,每次生成都是独立的. 现在有一个 X,初始为 0.每次操作生成一个随机数 v 并将 X 异或 v. 对于每一个 i ∈ [0

@atcoder - [email&#160;protected] Simple Subsequence Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定由若干长度 <= N 的 01 字符串组成的集合 S.请找到长度最长的串 t(如果有多个选字典序最小的),使得存在 >= K 个 S 中的字符串,使得 t 是这些字符串的子序列. 原题题面. @[email protected] 先看看怎么检验串 t 是否为某个串 s 的子序列:

@atcoder - [email&#160;protected] Two Trees

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定两棵树 A, B.现你需要构造一组值 (X1, X2, ..., XN)(两棵树编号相同的点对应权值相同),使得两棵树内任意子树的权值和的绝对值为 1. 无解输出 IMPOSSIBLE. 原题链接. @[email protected] 由于权值和为 1,可以推算出每个结点的奇偶性

@atcoder - [email&#160;protected] Next or Nextnext

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 N 的序列 a,问有多少排列 p,满足对于每一个 i,都有 \(a_i = p_i\) 或 \(a_i = p_{p_i}\) 成立. 原题传送门. @[email protected] 为了更直观地理解问题,不妨建个图:连有向边 \(i -> p_i\). 对于任意序

@atcoder - CODE FESTIVAL 2017 Final - [email&#160;protected] Tree MST

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定 N 个点,第 i 点有一个点权 Xi,再给定一棵边带权的树,第 i 条 (Ai, Bi) 边权为 Ci. 构建一个完全图,完全图中边 (i, j) 的边权为 dist(i, j) + Xi + Xj,其中 dist(i, j) 是点 i 与点 j 在树上的距离. 求该完全图的最小

$*和[email&#160;protected]之间区别代码分析

#!/bin/bash set 'apple pie' pears peaches for i in $*           /*单引号被去掉,循环单个字符输出*/ do echo $i done [[email protected] Ex_14.02-14.31]# sh 14-14-1 apple pie pears peaches -------------------------------------------------------------- #!/bin/bash set

[email&#160;protected]一个高效的配置管理工具--Ansible configure management--翻译(六)

无书面许可请勿转载 高级playbook Finding files with variables All modules can take variables as part of their arguments by dereferencing them with {{ and }} . You can use this to load a particular file based on a variable. For example, you might want to select a