Mex is a function on a set of integers, which is universally used for impartial game theorem. For a non-negative integer set S, mex(S) is defined as the least non-negative integer which is not appeared in S. Now our problem is about mex function on a sequence.
Consider a sequence of non-negative integers {ai}, we define mex(L,R) as the least non-negative integer which is not appeared in the continuous subsequence from aL to aR, inclusive. Now we want to calculate the sum of mex(L,R) for all 1 <= L <= R <= n.
Input
The input contains at most 20 test cases.
For each test case, the first line contains one integer n, denoting the length of sequence.
The next line contains n non-integers separated by space, denoting the sequence.
(1 <= n <= 200000, 0 <= ai <= 10^9)
The input ends with n = 0.
Output
For each test case, output one line containing a integer denoting the answer.
Sample Input
3
0 1 3
5
1 0 2 0 1
0
Sample Output
5
24
Hint
For the first test case:
mex(1,1)=1, mex(1,2)=2, mex(1,3)=2, mex(2,2)=0, mex(2,3)=0,mex(3,3)=0.
1 + 2 + 2 + 0 +0 +0 = 5.
题意:定义一个mex(l,r)为l到r没有出现过的最小的整数。问区间所有的mex的和是多少?
思路:我们按着题意把区间的所有mex求出来,会发现所有mex的值是呈现非递减序列的。我们先把(1,1)~(1,n)的所有mex值求出来。现在我们要求(2,2) ~ (2,n)的所有mex,要是再按着1的那样求肯定会超时。那我们看a[1]的消失对于2之后的mex有什么影响呢?对于mex<=a[1]的点来说,是没有影响的。但是对于大于a[1]的点来说,是有影响的,它们的值会变成a[1]。但是这个影响会持续到哪儿呢,会持续到下个a[1]再次出现。现在我们把所有节点的值下次出现的时机记录下来。在线段树上区间更新值,记录sum和最大值。并且在线段树上找二分第一个大于a[i]的值。
代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxx=2e5+100;
struct node{
int l;
int r;
ll _max;
ll lazy;
ll sum;
}p[maxx<<2];
int a[maxx],mex[maxx],nxt[maxx];
int n;
inline void pushup(int cur)
{
p[cur].sum=p[cur<<1].sum+p[cur<<1|1].sum;
p[cur]._max=max(p[cur<<1]._max,p[cur<<1|1]._max);
}
inline void pushdown(int cur)
{
if(p[cur].lazy!=-1)
{
p[cur<<1].lazy=p[cur<<1|1].lazy=p[cur].lazy;
p[cur<<1]._max=p[cur<<1|1]._max=p[cur].lazy;//别忘了更新_max
p[cur<<1].sum=(ll)(p[cur<<1].r-p[cur<<1].l+1)*p[cur].lazy;
p[cur<<1|1].sum=(ll)(p[cur<<1|1].r-p[cur<<1|1].l+1)*p[cur].lazy;
p[cur].lazy=-1;
}
}
inline void build(int l,int r,int cur)
{
p[cur].l=l;
p[cur].r=r;
p[cur].sum=p[cur]._max=0;
p[cur].lazy=-1;
if(l==r)
{
p[cur].sum=p[cur]._max=(ll)mex[l];
return ;
}
int mid=l+r>>1;
build(l,mid,cur<<1);
build(mid+1,r,cur<<1|1);
pushup(cur);
}
inline void update(int l,int r,ll v,int cur)
{
int L=p[cur].l;
int R=p[cur].r;
if(l<=L&&R<=r)
{
p[cur]._max=v;
p[cur].sum=(ll)(R-L+1)*v;
p[cur].lazy=v;
return ;
}
pushdown(cur);
int mid=L+R>>1;
if(r<=mid) update(l,r,v,cur<<1);
else if(l>mid) update(l,r,v,cur<<1|1);
else update(l,mid,v,cur<<1),update(mid+1,r,v,cur<<1|1);
pushup(cur);
}
inline int query(ll v,int cur)
{
int L=p[cur].l;
int R=p[cur].r;
if(L==R) return L;
pushdown(cur);
if(p[cur<<1]._max>v) return query(v,cur<<1);//优先找左儿子
else return query(v,cur<<1|1);
}
int main()
{
int l,r;
while(scanf("%d",&n)&&n)
{
map<int,int> mp;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int cnt=0;
for(int i=1;i<=n;i++)
{
mp[a[i]]=1;
while(mp.find(cnt)!=mp.end()) cnt++;
mex[i]=cnt;
}
mp.clear();
for(int i=n;i>=1;i--)//记录每个值下次出现的位置
{
if(mp[a[i]]==0) nxt[i]=n+1;
else nxt[i]=mp[a[i]];
mp[a[i]]=i;
}
build(1,n,1);
ll sum=0;
for(int i=1;i<=n;i++)
{
sum+=p[1].sum;
if(a[i]<p[1]._max)//必须有,因为如果不满足这个条件,就会找出来最右边的点,那样就不对了。
{
l=query((ll)a[i],1);
r=nxt[i]-1;
if(l<=r) update(l,r,(ll)a[i],1);
}
update(i,i,0ll,1);//把当前值更新为0.
}
printf("%lld\n",sum);
}
return 0;
}
努力加油a啊,(o)/~