YACS 2022年9月月赛 甲组 T1 游戏体验 题解

发布时间 2023-03-31 19:34:56作者: Xy_top

题目链接

最近很有空,我填坑来了(

思路

这道题目有一个很困难的限制:重复玩的角色会让它带来的快乐值清零。我们考虑如何消去这个限制。

考虑如下方法:假如我们考虑 $1\cdots$ $r$ 玩的最大值。
区间内的最后一个 $x$ 类型角色玩它得到的快乐值是 $c_x$,倒数第二个 $x$ 类型角色玩它得到的快乐值是 $-c_x$
(这样意味着如果玩了这两个,那么快乐值就没了。)

其他的 $x$ 类型角色,因为都在这两个前面,想玩的话快乐值已经被清空了所以玩它得到的快乐值为 $0$,那么我们枚举选择区间的右端点,处理一下,就成了动态维护全体最大子段和。

模拟

如果没看懂,那我们来模拟一下样例:

3
3 6 2
6
1 3 1 1 2 1


首先枚举右端点 $r=1$,出现了一个新角色 $1$,目前的快乐值序列为:

3 0 0 0 0 0,最大子段和为 $3$

枚举右端点 $r=2$,又出现了一个新角色 $3$,快乐值序列为:
3 2 0 0 0 0,最大子段和为 $5$。

枚举右端点 $r=3$,此时出现了一个重复的 $1$,那么前面的那个快乐值变成负的。新的快乐值序列为:
-3 2 3 0 0 0,最大子段和为 $5$。

枚举右端点 $r=4$,此时又出现了一个 $1$,倒数第三个快乐值没了,倒数第二个要变成负的,新的快乐值序列为:
0 2 -3 3 0 0,最大子段和为 $3$。
(这里你可能会觉得有些问题,但你模拟一下游戏区间右端点为 $4$ 的情况你就知道没错。)

枚举右端点 $r=5$,这时出现了一个新的角色 $2$,快乐值序列变为:
0 2 -3 3 6 0,最大子段和为 $9$。

最后枚举右端点 $r=6$,又来了一个 $1$,快乐值序列变为:
0 2 0 -3 6 3,最大子段和为 $9$。

输出的答案为其中最大的一个,即 $9$,至此,这篇题解的大部分内容结束。

闲话

此题的数据范围是 $10^6$,首先要开 $LL$。其次,凭借着线段树巨大的常数,我们收获了一个 TLE,所以我们需要加最快的快读,$fread$:

char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread (buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : * p1 ++)
inline int rd () {
    int x = 0;
    char ch = getchar ();
    while (!isdigit(ch) ) ch = getchar ();
    while (isdigit(ch) ) {
        x = x * 10 + (ch ^ 48);
        ch = getchar ();
    }
    return x;
}

当然,线段树的常数毕竟大,所以还是有几个点 TLE,我们加上 register,inline 还是有可能会超时,再加上卡常火车头勉强过了~

卡常火车头,(效果超强,慎用)本地机子运行能快个 $3$ 倍,当然加上进一步的指令集优化能够更快。

#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")

最后是 $AC$ 代码:

#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include <iostream>
#define int long long
using namespace std;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread (buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : * p1 ++)
inline int read() {
    int x = 0;
    char ch = getchar ();
    while (!isdigit(ch) ) ch = getchar ();
    while (isdigit(ch) ) {
        x = x * 10 + ch - 48;
        ch = getchar ();
    }
    return x;
}
int n, m, t1, t2, ans;
int c[1000005], x[1000005], last[1000005][2];
struct Segment_tree {int l, r, val, sum;}a[4000005];
inline void pushup (int l, int r, int k) {
    a[k].l = max (a[k << 1].l, a[k << 1].sum + a[k << 1 | 1].l);
    a[k].r = max (a[k << 1 | 1].r, a[k << 1 | 1].sum + a[k << 1].r);
    a[k].val = max (a[k << 1].r + a[k << 1 | 1].l, max (a[k << 1].val, a[k << 1 | 1].val) );
    a[k].sum = a[k << 1].sum + a[k << 1 | 1].sum;
}
inline void update (int l, int r, int k) {
    if (l == r) {
        a[k].sum = a[k].l = a[k].r = a[k].val = t2;
        return;
    }
    int mid = l + r >> 1;
    if (t1 <= mid) update (l, mid, k << 1);
    else update (mid + 1, r, k << 1 | 1);
    pushup (l, r, k);
}
signed main () {
    n = read ();
    for (int i = 1; i <= n; i ++) c[i] = read ();
    m = read ();
    for (int i = 1; i <= m; i ++) x[i] = read ();
    for (int i = 1; i <= m; i ++) {
        t1 = last[x[i] ][1], t2 = 0;
        if (t1 != 0) update (1, m, 1);
        t1 = last[x[i] ][0], t2 = -c[x[i] ];
        if (t1 != 0) update (1, m, 1);
        t1 = i, t2 = c[x[i] ];
        update (1, m, 1);
        last[x[i] ][1] = last[x[i] ][0];
        last[x[i] ][0] = i;
        ans = max (ans, a[1].val);
    }
    printf ("%lld", ans);
    return 0;
}