安吉D10-T3
- 1安吉D19 T1
- 2安吉D19 T3
- 3安吉D20 模考12总结
- 4安吉D17 T3
- 5安吉D17 T4
- 6安吉D19 模考11总结
- 7安吉D15 A
- 8安吉D16 T4
- 9安吉D17 T1
- 10安吉D17 模考10总结
- 11安吉D16 T3
- 12安吉D16 模考9总结
- 13安吉D13 模考8总结
- 14安吉D13-T2
- 15安吉D13-T3
- 16安吉D12-T3
- 17安吉D11-B
- 18安吉D11-C
- 19安吉D11-D
- 20安吉D12 模考7总结
- 21安吉D12-T2
- 22安吉D11-A
- 23安吉D10 模考6总结
- 24安吉D10-T3本文
- 25安吉D10-T4
- 26安吉D8-E
- 27安吉D9-T4
- 28安吉D8-A
- 29安吉D8-B
- 30安吉D8-C
- 31安吉D8-D
- 32安吉D9 模考5总结
- 33安吉D9-T2
- 34安吉D9-T3
- 35安吉D6-T4
- 36安吉D4-G
- 37安吉D5-T2
- 38安吉D6 模考4总结
- 39安吉D6-T1
- 40安吉D6-T3
- 41安吉-开营测试 T4
- 42安吉D5 模考3总结
- 43安吉D5-T3
- 44安吉D4-A
- 45安吉D4-B
- 46安吉D4-C
- 47安吉D4:容斥原理
- 48安吉Day4-D
- 49安吉D3 模考2总结
- 50安吉D3-T1
- 51安吉D3-T2
- 52安吉D3-T4
- 53安吉D2 模考1总结
- 54安吉D2-T2
- 55安吉D1-G
- 56安吉D1-L
- 57安吉D2-T4
原题呈现
我爱喝奶茶
题目背景
小信家附近开了一家奶茶店,从此他迷上了喝奶茶.
题目描述
奶茶店共有 款特色限定奶茶,编号为 ,第 款奶茶给小信带来的愉悦度为 .
在 天之中,奶茶店每天只会上架其中一款限定奶茶,第 天上架的是第 款.
小信可以选择打卡连续的 天内上架的所有奶茶.
但是小信是一个非常喜新厌旧的人,一旦同一款奶茶打卡多于一次,就会厌恶这款奶茶,甚至忘记奶茶曾经带给他的快乐(即仅当该款奶茶在区间内恰好只打卡过 1 次时,才能获得其愉悦程度 ).
现在,请问小信怎么打卡能让他的愉悦程度最大,最大的愉悦度是多少.
输入格式
第一行两个整数 .
第二行包含 个整数,第 个表示 .
第三行包含 个整数,第 个表示 .
输出格式
一行三个整数,分别表示打卡的区间 ,以及最大打卡收益,用空格隔开.如果有多个区间满足条件,输出区间长度最短且 最小的一个.
样例
样例输入
9 42 3 1 1 4 1 2 4 15 3 6 6样例输出
1 5 15数据范围
对于 的数据:
对于 的数据: 其中有 的数据满足每种奶茶最多出现两次
对于 的数据:,,
我们枚举起点,对于每一个起点 ,对从第 天起的每一天的奶茶的权值做一次前缀和,其中,第二次出现的奶茶权值为 ,第三次出现的奶茶权值为 .
如下面的例子:
9 42 3 1 1 4 1 2 4 11 2 3 4则以第一天为起点每一天出现的奶茶的权值列表为:
2 3 1 1 4 1 2 4 1将其中第二次出现的奶茶权值置为 ,第三次出现的奶茶权值置为 ,则可得:
2 3 1 -1 4 0 -2 -4 0将其做一次前缀和:
2 5 6 5 9 9 7 3 3前缀和数组中最大的值(设其下标为 ,此例中 )即为起点为 1 时的最大答案,区间为 .
再如,第二天为起点每一天出现的奶茶的权值列表为:
3 1 1 4 1 2 4 1将其中第二次出现的奶茶权值置为 ,第三次出现的奶茶权值置为 ,则可得:
3 1 -1 4 0 2 -4 0将其做一次前缀和:
3 4 3 7 7 9 5 5前缀和数组中最大的值(设其下标为 ,此例中 )即为起点为 2 时的最大答案,区间为 .
对于每一天作为起点都这么操作一次,取起点为 答案的最大值,时间复杂度为 ,考虑优化.
枚举起点好像没有优化空间,但是如何快速处理前缀和呢?
仔细观察原数组在 时有什么变化:
- 原数组中第一个奶茶种类为
a[i]的奶茶(设其下标为 )消失了. - 原数组中第二个奶茶种类为
a[i]的奶茶(设其下标为 )权值取了相反数,变为w[a[i]]. - 原数组中第三个奶茶种类为
a[i]的奶茶(设其下标为 )权值从 0 变为-w[a[i]].
对应到前缀和数组(设大小为 )上就是:
- 区间 中每一项减少
w[a[i]]. - 区间 中每一项增加
2*w[a[i]]. - 区间 中每一项减少
w[a[i]].
区间修改使用线段树优化,就可以在 的复杂度内更新前缀和数组.
至于区间,只需要在线段树上二分查找:
- 对于每一个线段树上的节点,比较其两个子节点的
max哪一个更大,选择更大的那个节点,在该子节点中继续查找,直到找到大小为 1 的节点.
标程
#include <bits/stdc++.h>typedef long long ll;using namespace std;const int N = 1e6 + 100;
int n, m, a[N], w[N], l, r;ll s[N];ll ans = -1;
vector<int> vec[N];int pointer[N];
class SegmentTree { public: struct Node { int l, r; ll max; ll add; } f[N * 5]; void Build(int t, int l, int r) { f[t].l = l; f[t].r = r; if (l == r) { f[t].max = s[l]; return; }
int mid = (l + r) / 2; Build(t * 2, l, mid); Build(t * 2 + 1, mid + 1, r);
pushup(t); } void Add(int t, int x, int y, ll v) { pushdown(t); if (x <= f[t].l && f[t].r <= y) { f[t].add += v; f[t].max += v; return; } if (x > f[t].r || y < f[t].l) { return; }
Add(t * 2, x, y, v); Add(t * 2 + 1, x, y, v);
pushup(t); } ll Query(int t, int x, int y) { pushdown(t);
if (x <= f[t].l && f[t].r <= y) { return f[t].max; } if (x > f[t].r || y < f[t].l) { return -1; }
return max(Query(t * 2, x, y), Query(t * 2 + 1, x, y)); } int findmax(int t) { if (f[t].l == f[t].r) { return f[t].l; } if (Query(t * 2, 1, n) >= Query(t * 2 + 1, 1, n)) { return findmax(t * 2); } return findmax(t * 2 + 1); }
private: void pushup(int t) { f[t].max = max(f[t * 2].max, f[t * 2 + 1].max); } void pushdown(int t) { if (f[t].add) { ll add = f[t].add; f[t * 2].add += add; f[t * 2].max += add; f[t * 2 + 1].add += add; f[t * 2 + 1].max += add; f[t].add = 0; } }} tr;
signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> a[i]; } for (int i = 1; i <= m; i++) { cin >> w[i]; } for (int i = 1; i <= n; i++) { vec[a[i]].push_back(i); int wt = w[a[i]]; if (vec[a[i]].size() == 2) { wt = -w[a[i]]; } if (vec[a[i]].size() > 2) { wt = 0; } s[i] = s[i - 1] + wt; } tr.Build(1, 1, n); for (int i = 1; i <= n; i++) { ll tot = tr.Query(1, 1, n); int newR = tr.findmax(1); int nowLen = newR - i + 1; int miniLen = r - l + 1; if (tot > ans || (tot == ans && nowLen < miniLen)) { ans = tot; l = i; r = newR; }
int wt = w[a[i]]; tr.Add(1, i, n, -wt); pointer[a[i]]++; if (pointer[a[i]] != vec[a[i]].size()) { int top = vec[a[i]][pointer[a[i]]]; tr.Add(1, top, n, 2ll * wt); pointer[a[i]]++; if (pointer[a[i]] != vec[a[i]].size()) { int top = vec[a[i]][pointer[a[i]]]; tr.Add(1, top, n, -wt); } pointer[a[i]]--; } } cout << l << " " << r << " " << ans; return 0;}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


