题意:
https://www.lydsy.com/JudgeOnline/problem.php?id=3747
有 m 个不同颜色的点,每个点有个权值,现在由这 m 种颜色的点组成的长度为 n 的序列
求一个区间,这个区间内只出现一次的点的权值和最大
参考博客:http://hzwer.com/5715.html
分析
这种和颜色出现次数相关的题比较正常的想法就是枚举左端点,
在右移左端点的时候,处理“右移”这个操作对[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\n",ans); }