推式子
是的,努力推式子即可。
记需要统计答案的区间的头尾值为v,主要思想就是分不同的v来考虑。枚举v的所有区间下的最小间隔的区间,考虑区间内的数在v的所有跨过这个区间的大区间的贡献即可。注意中间端点的答案会被统计两次,需要减掉……(刚开始没注意到这一点然后WA飞啦)
#include<cstdio> #include<cstring> #define N 1000005 #define uint unsigned using namespace std; namespace runzhe2000 { int read() { int r = 0; char c = getchar(); for(; c < '0' || c > '9'; c = getchar()); for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar()); return r; } int n; uint a[N], b[N], suma[N], sumb[N], suml[N], sumr[N], cntl[N], cntr[N], lastl[N], lastr[N], lastv[N], ans; void main() { n = read(); for(int i = 1; i <= n; i++) { a[i] = read(); b[i] = -2*a[i]*i; suma[i] = suma[i-1] + a[i]; sumb[i] = sumb[i-1] + b[i]; } for(int i = 1; i <= n; i++) { int p = lastv[a[i]]; suml[i] = suml[p] + i; cntl[i] = cntl[p] + 1; lastl[i] = p; lastv[a[i]] = i; } memset(lastv,0,sizeof(lastv)); for(int i = n; i >= 1; i--) { int p = lastv[a[i]]; sumr[i] = sumr[p] + i; cntr[i] = cntr[p] + 1; lastr[i] = p; lastv[a[i]] = i; } for(int i = 1; i <= n; i++) if(lastl[i]) { int j = lastl[i]; ans -= (suma[i] - suma[j-1]) * (suml[j] * cntr[i] + sumr[i] * cntl[j]); ans -= (sumb[i] - sumb[j-1]) * cntl[j] * cntr[i]; if(lastr[i]) { int k = lastr[i]; ans += b[i] * cntl[j] * cntr[k] + a[i] * (suml[j] * cntr[k] + sumr[k] * cntl[j]); } } printf("%u\n",ans); } } int main() { runzhe2000::main(); }