https://www.luogu.com.cn/problem/P7530
按照套路,我们记\(pre[i]\)表示上一个和\(i\)相同颜色的位置
考虑扫描线,记\(f[l]\)为每个左端点的答案,用线段树维护就是区间\((pre[r],r-1)\)的\(f[l]\)和
考虑\(i\in[l,r],pre[i]\)显然能作为左端点,把它当系数设为0,假设\(i\)是中间点,那么它可以把\((pre[i],i)\)中可以作为左端点的\(f[l]\)全部加\(1\)
写个带系数的线段树维护即可
code:
#include<bits/stdc++.h>
#define ll long long
#define N 400050
using namespace std;
#define ls (rt << 1)
#define rs (rt << 1 | 1)
ll s[N << 2], val[N << 2], tg[N << 2], sz[N << 2];
void update(int rt) {
s[rt] = s[ls] + s[rs], sz[rt] = sz[ls] + sz[rs];
}
void padd(int rt, int o) {
tg[rt] += o, s[rt] += o * sz[rt], val[rt] += o;
}
void pushdown(int rt) {
if(tg[rt]) {
padd(ls, tg[rt]), padd(rs, tg[rt]);
tg[rt] = 0;
}
}
void addx(int rt, int l, int r, int x, int o) {
if(l == r) {
sz[rt] += o;
s[rt] += o * val[rt];
return ;
}
pushdown(rt);
int mid = (l + r) >> 1;
if(x <= mid) addx(ls, l, mid, x, o);
else addx(rs, mid + 1, r, x, o);
update(rt);
}
void add(int rt, int l, int r, int L, int R, int o) {
if(L > R) return ;
if(L <= l && r <= R) {
padd(rt, o);
return ;
}
pushdown(rt);
int mid = (l + r) >> 1;
if(L <= mid) add(ls, l, mid, L, R, o);
if(R > mid) add(rs, mid + 1, r, L, R, o);
update(rt);
}
ll query(int rt, int l, int r, int L, int R) {
if(L > R) return 0;
if(L <= l && r <= R) return s[rt];
pushdown(rt);
int mid = (l + r) >> 1; ll ret = 0;
if(L <= mid) ret = query(ls, l, mid, L, R);
if(R > mid) ret += query(rs, mid + 1, r, L, R);
return ret;
}
int n, a[N], pre[N], mp[N];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]), pre[i] = mp[a[i]], mp[a[i]] = i;
ll ans = 0;
for(int i = 1; i <= n; i ++) {
if(pre[i])
addx(1, 1, n, pre[i], - 1),
add(1, 1, n, pre[pre[i]] + 1, pre[i] - 1, - 1);
ans += query(1, 1, n, pre[i] + 1, i - 1);
addx(1, 1, n, i, 1);
add(1, 1, n, pre[i] + 1, i - 1, 1);
}
printf("%lld", ans);
return 0;
}