HDU-4747 Mex(线段树区间更新)

题目大意:给一个长度为n的整数序列,定义mex(i,j)表示区间[i,j]中没有出现过的最小非负整数,求sigma(mex(i,j)),即序列中所有连续非空子区间的mex之和。

题目分析:

answer=mex(1,1)+mex(1,2)...mex(1,n)
+ mex(2,2)...mex(2,n)
.
.
.
+ mex(n,n)。

  初始时,用线段树的叶子节点维护mex(1,i),将a(1)从序列中拿去后,将叶子节点的维护信息更新为mex(2,i),以此类推...没更新一次,便求一次区间和,总和即为答案。

  当拿掉a(i)后,只有a(k)以前的mex(i+1,j)将会受到影响,其中k>i并且a(i)=a(k)。

  当i+1<=j<k时,如果mex(i,j)>a(i),那么mex(i+1,j)=a(i),否则mex(i+1,j)=mex(i,j)。显然,当 i 固定时,mex(i,j)关于 j 单调不减。所以,mex(i,j)>a(i)的将会是一段区间。

代码如下:

# include<bits/stdc++.h>
using namespace std;
# define LL long long
# define mid (l+(r-l)/2)

const int N=200000;

int a[N+5];
int mex[N+5];
int nxt[N+5];
LL tr[N*4+5];
LL lazy[N*4+5];
int maxn[N*4+5];

inline void getMex(int n)
{
	map<int,int>mp;
	map<int,int>f;
	fill(nxt,nxt+1+n,n+1);
	int m=0;
	for(int i=1;i<=n;++i){
		mp[a[i]]=1;
		while(mp[m]) ++m;
		mex[i]=m;
		if(f[a[i]])
			nxt[f[a[i]]]=i;
		f[a[i]]=i;
	}
}

inline void pushUp(int rt)
{
	tr[rt]=tr[rt<<1]+tr[rt<<1|1];
	maxn[rt]=max(maxn[rt<<1],maxn[rt<<1|1]);
}

inline void pushDown(int rt,int l,int r)
{
	if(lazy[rt]!=-1){
		lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
		maxn[rt<<1]=maxn[rt<<1|1]=lazy[rt];
		tr[rt<<1]=lazy[rt]*(LL)(mid-l+1);
		tr[rt<<1|1]=lazy[rt]*(LL)(r-mid);
		lazy[rt]=-1;
	}
}

inline void build(int rt,int l,int r)
{
	lazy[rt]=-1;
	if(l==r){
		tr[rt]=mex[l];
		maxn[rt]=mex[l];
	}else{
		build(rt<<1,l,mid);
		build(rt<<1|1,mid+1,r);
		pushUp(rt);
	}
}

inline int query_up(int rt,int l,int r,int L,int R,int x)
{
	if(l==r){
		if(tr[rt]<x) return 0;
		return l;
	}
	pushDown(rt,l,r);
	if(L<=mid&&x<maxn[rt<<1]){
		int k=query_up(rt<<1,l,mid,L,R,x);
		if(k>0) return k;
	}
	if(R>mid&&x<maxn[rt<<1|1]){
		int k=query_up(rt<<1|1,mid+1,r,L,R,x);
		if(k>0) return k;
	}
	return 0;
}

inline void update(int rt,int l,int r,int L,int R,int x)
{
	if(L<=l&&r<=R){
		lazy[rt]=x;
		tr[rt]=(LL)x*(LL)(r-l+1);
		maxn[rt]=x;
	}else{
		pushDown(rt,l,r);
		if(L<=mid) update(rt<<1,l,mid,L,R,x);
		if(R>mid) update(rt<<1|1,mid+1,r,L,R,x);
		pushUp(rt);
	}
}

inline LL query(int rt,int l,int r,int L,int R)
{
	if(L<=l&&r<=R) return tr[rt];
	pushDown(rt,l,r);
	LL res=0;
	if(L<=mid) res+=query(rt<<1,l,mid,L,R);
	if(R>mid) res+=query(rt<<1|1,mid+1,r,L,R);
	return res;
}

int main()
{
	int n;
	while(scanf("%d",&n)&&n)
	{
		for(int i=1;i<=n;++i) scanf("%d",a+i);
		getMex(n);
		build(1,1,n);
		LL ans=query(1,1,n,1,n);
		for(int i=1;i<n;++i){
			int p=query_up(1,1,n,i+1,nxt[i]-1,a[i]);
			if(p>0) update(1,1,n,p,nxt[i]-1,a[i]);
			ans+=query(1,1,n,i+1,n);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

  

时间: 2024-10-04 06:36:13

HDU-4747 Mex(线段树区间更新)的相关文章

HDU 3308 LCIS 线段树 区间更新

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目描述: 有两种操作, U x y  , 第xth赋值为y .Q x y , 查询区间x-y的最长连续上升子序列的长度L 解题思路: 对于线段树不好的我依然好难.....有太多细节需要注意了....但是这是一道很好的题, 一段区间的L可能从三个地方来, 一种是中间, 一种是以左起点为开头的, 一种是以右起点结尾的, 这样查询的时候就要注意了: 如果两段的中间值是a[m] < a[m+1]

hdu 1698(线段树区间更新)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 30080    Accepted Submission(s): 14859 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible thing

hdu 4747 Mex( 线段树? 不,区间处理就行(dp?))

Mex Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 3056    Accepted Submission(s): 1006 Problem Description Mex is a function on a set of integers, which is universally used for impartial game

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

HDU 5023 A Corrupt Mayor&#39;s Performance Art 线段树区间更新+状态压缩

Link:  http://acm.hdu.edu.cn/showproblem.php?pid=5023 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <vector> 6 #include <string> 7 #include <cmath> 8 using namesp

HDU 5023 A Corrupt Mayor&#39;s Performance Art(线段树区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5023 解题报告:一面墙长度为n,有N个单元,每个单元编号从1到n,墙的初始的颜色是2,一共有30种颜色,有两种操作: P a b c  把区间a到b涂成c颜色 Q a b 查询区间a到b的颜色 线段树区间更新,每个节点保存的信息有,存储颜色的c,30种颜色可以压缩到一个int型里面存储,然后还有一个tot,表示这个区间一共有多少种颜色. 对于P操作,依次往下寻找,找要更新的区间,找到要更新的区间之前

HDU 4902 Nice boat(线段树 区间更新)

Nice boat 大意:给你一个区间,每次可以进行两种操作,1:把区间中的数全都变成x  2:把区间中大于x的数变成gcd(a[i], x),最后输出序列. 思路:线段树成段更行,用num数组的叶子存储数据,节点当作lazy来使用. 1 #include <stdio.h> 2 const int maxn = 100005; 3 4 int num[maxn<<2]; 5 6 int gcd(int a, int b){ 7 return b?gcd(b, a%b):a; 8

HDU 1698 Just a Hook (线段树,区间更新)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 17214    Accepted Submission(s): 8600 Problem Description In the game of DotA, Pudge’s meat hook is actually the most horrible thing f

HDU 1698 Just a Hook (线段树 区间更新基础)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 21856    Accepted Submission(s): 10963 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible thing

hdu 5023 A Corrupt Mayor&#39;s Performance Art(线段树区间更新)

#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; int tree[5001000],add[5001000]; int color[50]; int n,m; void pushup(int pos) { tree[pos]=tree[pos<<1]|tree[pos<<1|1]; //更新