经典颜色问题推荐博文
https://www.cnblogs.com/tyner/p/11519506.html
https://www.cnblogs.com/tyner/p/11616770.html
https://www.cnblogs.com/tyner/p/11620894.html
题意:
https://www.lydsy.com/JudgeOnline/problem.php?id=3747
有 m 个不同颜色的点,每个点有个权值,现在由这 m 种颜色的点组成的长度为 n 的序列
求一个区间,这个区间内只出现一次的点的权值和最大
参考博客:http://hzwer.com/5715.html
分析
这种和颜色出现次数相关的题比较正常的想法就是枚举左端点,并计算出nxt[i]
(与坐标 i 的颜色相同的下一个位置坐标,
在右移左端点的时候,处理“右移”这个操作对[l, nxt[l]-1] 以及 [nxt[l] , n] 这两个区间中答案的的影响。
注意可能有颜色不在序列中的情况
这题数据要开longlong
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const int MAX = 1000000+99;
int n,m;
ll ans;
int nxt[MAX], lst[MAX];
int f[MAX], w[MAX];
struct tree{
ll add, mx;
}tr[MAX<<2];
void pushup(int o) {tr[o].mx = max(tr[o<<1].mx, tr[o<<1|1].mx);}
//void build(){}
void pushdown(int o) {
if(0 == tr[o].add) return ;
tr[o<<1].add += tr[o].add;
tr[o<<1|1].add += tr[o].add;
tr[o<<1].mx += tr[o].add;
tr[o<<1|1].mx += tr[o].add;
tr[o].add = 0;
}
void optadd(int o, int l, int r, int ql, int qr, int k) {
if(ql<=l && r<=qr) {
tr[o].add += k;
tr[o].mx += k;
return ;
}
int mid = (l+r)>>1;
pushdown(o);
if(ql <= mid) optadd(o<<1, l, mid, ql, qr, k);
if(mid < qr) optadd(o<<1|1, mid+1, r, ql, qr, k);
pushup(o);
}
int main() {
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",&f[i]);
for(int j = 1; j <= m; j++) scanf("%d",&w[j]);
for(int i = n; i >= 1; i--) {
nxt[i] = lst[f[i]];
lst[f[i]] = i;
}
for(int i = 1; i <= m; i++) //先初始化左端点为 1 的情况(即处理每种颜色的第一个位置对答案的影响)
if(lst[i]) {//注意判是否在序列中存在这个颜色(万一没有呢)
if(!nxt[lst[i]]) optadd(1, 1, n, lst[i], n, w[i]);//别越界了,时刻注意nxt,lst的含义
else optadd(1, 1, n, lst[i], nxt[lst[i]]-1, w[i]);
}
int t;
for(int i = 1; i <= n; i++) {
ans = max(ans, tr[1].mx);//当左端点为 i 时,去的最大答案
t = nxt[i];//以下表示左端点移到 i+1 后对答案的影响
if(t) {
optadd(1, 1, n, i, t-1, -w[f[i]]);
if(nxt[t]) optadd(1, 1, n, t, nxt[t]-1, w[f[i]]);
else optadd(1, 1, n, t, n, w[f[i]]);
} else optadd(1, 1, n, i, n, -w[f[i]]);
}
printf("%lld
",ans);
}