Mex HDU - 4747(线段树+二分)

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)/~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

starlet_kiss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值