HDU 4747 Mex

题意:求所有子区间Mex的和。Mex是最小的,不存在集合里的非负整数。例如{1,2,4}的Mex等于0。


思路:

记区间[l,r]的Mex值为M(l,r)。

考虑n个数M(1,1),M(1,2),M(1,3)...M(1,n)组成的序列,必然是非递减的,记这样的序列为S(1)。

先求出S(1)(在线段树中插好这n个数),接着要求出M(2,2),M(2,3)...M(2,n),即S(2)。S(1)变成S(2),需要把第一个数去掉(记为为x[1])。现在考虑去掉x[1]后对S(1)的影响。

1.如果M(1,r)<x[1],那么去掉后M(1,r)变成M(2,r),且值不变。

2.如果M(1,r)>x[1],且区间[2,r]中不存在x[1],那么去掉后M(2,r)=x[1]。

于是我们在序列S(1)中找到恰好大于x[1]的位置,记为p。那么M(2,2),M(2,3)...M(2,p-1)的值不变,与M(1,2),M(1,3)..M(1,p-1)一样。然后我们再找到下一个值为x[1]的位置,记为q。那么M(2,p)...M(2,q-1)的值都是x[1](相当于要修改区间[p,q-1]的值了)。

从S(1)到S(n)就是全部的答案了。

用线段树来维护上述操作,就是区间更新,求和,以及查找p。对于q,只要维护一个next数组就好,具体看代码。


code:

#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
 
/*-------------------------Template----*/
#define N  200020
#define E  100010
#define ll long long
#define CUBE(x) ((x)*(x)*(x))
#define SQ(x)     ((x)*(x))
#define ALL(x)     x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
#define maxAry(a,n) max_element(a,a+(n))
#define minAry(a,n) min_element(a,a+(n))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int PRIME =999983;
const int MOD   =10007;
const int MULTI =1000000007;
const double EPS=1e-9;
const int dx[]  = {0, 1, 0, -1};
const int dy[]  = {1, 0, -1, 0};
inline bool isodd(int x){return x&1;}
/*----------------------end Template----*/

class segTree{
#define lson (rt<<1)
#define rson (rt<<1|1)
#define rtl seg[rt].l
#define rtr seg[rt].r
private:
	struct segment{
		int l,r,max,mark;
		ll sum;
	}seg[N<<2];
public:
	void setVal(int val,int rt)
	{
		seg[rt].sum=1ll*(rtr-rtl+1)*val;
		seg[rt].max=seg[rt].mark=val;
	}
	void pushup(int rt)
	{
		seg[rt].sum=seg[lson].sum+seg[rson].sum;
		seg[rt].max=max(seg[lson].max,seg[rson].max);
	}
	void pushdown(int rt)
	{
		if(seg[rt].mark!=-1){
			setVal(seg[rt].mark,lson);
			setVal(seg[rt].mark,rson);
			seg[rt].mark=-1;
		}
	}
	void update(int val,int L,int R,int rt)
	{
		if(L<=rtl && rtr<=R){
			setVal(val,rt);
			return ;
		}
		int mid=(rtl+rtr)>>1;
		pushdown(rt);
		if(L<=mid) update(val,L,R,lson);
		if(mid<R) update(val,L,R,rson);
		pushup(rt);
	}
	ll query(int L,int R,int rt)
	{
		ll ans=0;
		if(L<=rtl && rtr<=R)
			return seg[rt].sum;
		int mid=(rtl+rtr)>>1;
		pushdown(rt);
		if(L<=mid) ans+=query(L,R,lson);
		if(mid<R) ans+=query(L,R,rson);
		return ans;
	}
	int up_bound(int val,int rt)
	{		
		if(rtl==rtr){
			if(seg[rt].max<val) return -1;
		 	return rtl;
		}
		pushdown(rt);
		if(val<seg[lson].max) 
			return up_bound(val,lson);
		else 
			return up_bound(val,rson);
	}
	void build(int l,int r,int rt)
	{
		rtl=l, rtr=r;
		seg[rt].mark=-1;
		if(l==r) return;
		int mid=(rtl+rtr)>>1;
		build(l,mid,lson);
		build(mid+1,r,rson);
	}
}T;

int n,a[N],d[N],Next[N];
bool vis[N];

int main()
{
	int mex;
	ll ans;
	while(scanf("%d",&n),n){
		CLR(vis,0);
		mex=ans=0;
		T.build(1,n,1);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			a[i]=a[i]>n?n+1:a[i];
			vis[a[i]]=true;
			if(a[i]==mex)
				for(int j=mex+1;j<=n;j++) if(!vis[j]){ 
					mex=j; break;
				}
			T.update(mex,i,i,1);
			ans+=mex;
		}
		CLR(d,-1);
		for(int i=n;i>=1;i--){
			if(d[a[i]]==-1) Next[i]=n+1;
			else Next[i]=d[a[i]];
			d[a[i]]=i;
		}
		for(int i=2;i<=n;i++){
			int l=T.up_bound(a[i-1],1);
			if(l!=-1) T.update(a[i-1],l,Next[i-1]-1,1);
			ans+=T.query(i,n,1);
		}
		printf("%I64d\n", ans);
	}
	return 0;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值