http://acm.sdut.edu.cn:8080/vjudge/contest/view.action?cid=216#problem/E
又是一道线段树神题。
对一个非负序列,定义mex(i,j)为区间[i,j]内没出现最小的正整数。求所有满足(1 <= i <= j <= n)的mex(i,j)的和。
以序列1 2 0 4 6 3 5 7 1 2 4 5 8为例,先把以i = 1的mex(1,j), i = 2的mex(2,j)列出来为
i = 1 0 0 3 3 3 5 7 8 8 8 8 8 9
i = 2 0 1 1 1 1 1 1 8 8 8 8 9
可以发现mex(2,j)与mex(1,j)有关系,就是在第一个1(a[1])与第二个1(a[9])之间的mex(1,j)>1(a[1])的数在mex(2,j)中都变为了1,而其余的mex(2,j)不变。所以只要找到了要变为1(a[1])的区间l和r,然后更新这一区间的值求和即可。
又可以发现对所有固定的i,mex(i,j)都是递增的,可以根据这一性质找到区间的左端点l,即第一个mex值大于a[1]的位置,而r可以设置一个next数组记录每个位置的数它的下一次出现的位置,那么r = next[i]-1。
所以,先初始化出mex(1,j)然后就依次求出以后的mex(i,j),用线段树维护区间的和和最大值。
#include <stdio.h> #include <iostream> #include <map> #include <set> #include <list> #include <stack> #include <vector> #include <math.h> #include <string.h> #include <queue> #include <string> #include <stdlib.h> #include <algorithm> //#define LL long long #define LL __int64 #define eps 1e-12 #define PI acos(-1.0) using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 200010; struct node { int l,r; int lazy; //lazy标记这一段区间的值是否相同 LL sum; //区间的和 LL mx;//区间的最大值 }tree[maxn*4]; int a[maxn]; int next[maxn]; int mex[maxn]; map<int,int>M; void push_up(int v) { if(tree[v].l == tree[v].r) return; tree[v].mx = max(tree[v*2].mx,tree[v*2+1].mx); tree[v].sum = tree[v*2].sum + tree[v*2+1].sum; } void push_down(int v) { if(tree[v].l == tree[v].r || tree[v].lazy == 0) return; tree[v*2].lazy = tree[v*2+1].lazy = 1; tree[v*2].mx = tree[v*2+1].mx = tree[v].mx; tree[v*2].sum = tree[v].mx * (tree[v*2].r - tree[v*2].l + 1); tree[v*2+1].sum = tree[v].mx * (tree[v*2+1].r - tree[v*2+1].l + 1); tree[v].lazy = 0; } void build(int v, int l, int r) { tree[v].l = l; tree[v].r = r; tree[v].lazy = 0; if(l == r) { tree[v].sum = mex[tree[v].l]; tree[v].mx = mex[tree[v].l]; return; } int mid = (l+r)>>1; build(v*2,l,mid); build(v*2+1,mid+1,r); push_up(v); } //找出mex值第一个大于a[i]的位置 int get(int v, int key) { if(tree[v].l == tree[v].r) return tree[v].l; push_down(v); if(key < tree[v*2].mx) return get(v*2,key); else return get(v*2+1,key); } void update(int v, int l, int r, int key) { if(tree[v].l == l && tree[v].r == r) { tree[v].mx = key; tree[v].lazy = 1; tree[v].sum = key * (tree[v].r - tree[v].l + 1); return; } push_down(v); int mid = (tree[v].l + tree[v].r) >> 1; if(r <= mid) update(v*2,l,r,key); else if(l > mid) update(v*2+1,l,r,key); else { update(v*2,l,mid,key); update(v*2+1,mid+1,r,key); } push_up(v); } int main() { int n,Min; while(~scanf("%d",&n)&&n) { for(int i = 1; i <= n; i++) scanf("%d",&a[i]); M.clear(); Min = 0; for(int i = 1; i <= n; i++) { M[a[i]] = 1; while(M.find(Min) != M.end()) Min++; mex[i] = Min; } M.clear(); for(int i = n; i >= 1; i--) { if(M.find(a[i]) == M.end()) next[i] = n+1; //next[i]而不是next[a[i]] else next[i] = M[a[i]]; M[a[i]] = i; } build(1,1,n); LL sum = 0; for(int i = 1; i <= n; i++) { sum += tree[1].sum; if(tree[1].mx > a[i]) { int l = get(1,a[i]); int r = next[i]; if(l < r) update(1,l,r-1,a[i]); } update(1,i,i,0); } printf("%I64d\n",sum); } return 0; }