题目
http://acm.hdu.edu.cn/showproblem.php?pid=4747
题意
给出一个序列,mex{}表示集合中没有出现的最小的自然数。然后 求sigma(mex (i , j)).
分析
在递推专题中看到这个题的,想了半天怎么感觉线段树可做啊,搜题解,果然线段树可做,递推做法有点神啊。
copy的题解:
考虑左端点固定时的所有区间的mex值,这个序列是一个非递减了。。。首先要明白。
初始就是求出mex[i]表示 mex(1 , i),对于每一个左端点,就是一个区间求和。
现在需要考虑的是左端点的改变对于序列的影响。。。
即左端点从i -> i + 1,mex[j]的改变。。。。即删去ai对于序列的影响。
如果 a[j] = a[i] 且 j > i ,不存在a[k] = a[i] j > k > i。即a[i]下一次出现的位置 。
根据mex的定义,我们知道 mex[k]不会改变, k >= j。因为删掉的ai还是存在于序列当中,所以不受影响。
之后需要考虑的是i +1 到 j - 1这段区间的mex值。。。删去了ai之后,使得原先mex值大于ai的,都会更新成ai。
很好理解。。。因为是没有出现的最小的,ai更小。。。
之前说过这是一个非递减的序列,所以原先mex值大于ai的也是一段连续的区间,所以我们可以找到最靠左的位置 r,使得mex[r] > a[i]。那么r 到 j - 1这段区间的mex值便 会更新为a[i]。
所以全部搞定。。。用线段树维护一下mex序列,区间更新,区间求和,然后一个查找就可以了。
代码
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define LL long long
const int maxn = 200005;
int add[maxn<<2];
int Max[maxn<<2];
int mex[maxn<<2];
LL sum[maxn<<2];
void PushUp(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
Max[rt] = max(Max[rt<<1] , Max[rt<<1|1]);
}
void PushDown(int rt,int m) {
if (add[rt]!=-1) {
add[rt<<1] = add[rt<<1|1] = add[rt];
Max[rt<<1] = Max[rt<<1|1] = add[rt];
sum[rt<<1] = add[rt] * (m - (m >> 1));
sum[rt<<1|1] = add[rt] * (m >> 1);
add[rt] = -1;
}
}
void build(int l,int r,int rt) {
add[rt] = -1;
if (l == r) {
Max[rt]=sum[rt]=mex[l];
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt) {
if (L <= l && r <= R) {
add[rt] = c;
Max[rt] = c;
sum[rt] = (LL)c * (r - l + 1);
return ;
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , c , lson);
if (m < R) update(L , R , c , rson);
PushUp(rt);
}
LL query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
LL ret = 0;
if (L <= m) ret += query(L , R , lson);
if (m < R) ret += query(L , R , rson);
return ret;
}
int getpos(int v,int l,int r,int rt)
{
if(l==r)return l;
PushDown(rt,r-l+1);
int m=(l+r)>>1;
if(Max[rt<<1] > v)return getpos(v, lson);
return getpos(v, rson);
}
int nex[maxn],a[maxn];
map<int,int>pre;
int main() {
int N;
while(~scanf("%d",&N)&&N){
pre.clear();
for(int i=1;i<=N;i++)nex[i]=N;
int Min=0;
for(int i=1;i<=N;i++){
scanf("%d",&a[i]);
int t=a[i];
if(pre.count(t))nex[pre[t]]=i-1;
pre[t]=i;
while(pre.count(Min))Min++;
mex[i]=Min;
}
build(1 , N , 1);
LL res=0;
for(int i=1;i<=N;i++){
res+=query(i,N,1,N,1);
if(Max[1]<a[i])continue;
int pos=getpos(a[i],1,N,1);
update(pos,nex[i],a[i],1,N,1);
}
printf("%lld\n", res);
}
return 0;
}