题目要求求某段区间第一个没有出现的数(0,1,2,3.。。。) ,对于所有的区间,我们把这样的数加起来最后得到一个结果。

首先,我们要求出这样的数,然后还得列举出所有的区间,复杂度太大了。

换种思路,我们定住L,是不是一次性能求出所有的R所得出的结果,这就用到线段树的性质了,因为在移动L的过程中,移一步只变化一个数,那么就可以用线段树进行维护。

首先求出[1,R] 以1为左端的所有区间的情况,记录每个点也就是1到那个点的这段区间值sum[i],以这个值建一颗树,那么在L向前移动的时候,每次丢掉一个数a[i-1], 因为少了这一个数,肯定后面有部分区间是变化的,有部分是不变化的,从这点开始向后找第一个与a[i-1]值相等的数的位置p,那么这个位置后面的sum肯定不会变化,因为丢掉的数又补上了。

那么就可以只考虑,从i位置到p这段里面的sum,如果原先的sum本来就比a[i-1]小,那说明a[i-1]的减少不影响它的值,所以不用改变,而所有大于a[i-1]的值将都变为a[i-1],这样更新一下,求和就可以了。

 #include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stdlib.h>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#define N 200010
#define LL __int64
#define INF 0xfffffff
const double eps = 1e-;
const double pi = acos(-1.0);
const double inf = ~0u>>;
vector<int>po[N];
map<int,int>f;
LL s[N<<];
int tm[N<<];
LL lz[N<<];
int sum[N];
int p[N],a[N];
bool vis[N];
void up(int w)
{
s[w] = s[w<<]+s[w<<|];
tm[w] = max(tm[w<<],tm[w<<|]);
//cout<<tm[w]<<" "<<w<<endl;
}
void down(int w,int m)
{
if(lz[w]!=-)
{
tm[w<<] = tm[w<<|] = lz[w];
lz[w<<] = lz[w<<|] = lz[w];
s[w<<] = lz[w]*(m-m/);
s[w<<|] = lz[w]*(m/);
lz[w] = -;
//cout<<lz[w]<<" <"<<w<<endl;
}
}
void build(int l,int r,int w)
{
if(l==r)
{
s[w] = sum[l];
tm[w] = sum[l];
return ;
}
int m = (l+r)>>;
build(l,m,w<<);
build(m+,r,w<<|);
up(w);
}
void update(int a,int b,int d,int l,int r,int w)
{
if(a<=l&&b>=r)
{
s[w] = (LL)d*(r-l+);
tm[w] = d;
lz[w] = d;
//cout<<l<<", "<<r<<" "<<tm[w]<<" "<<d<<endl;
return ;
}
int m = (l+r)>>;
down(w,r-l+);
if(a<=m) update(a,b,d,l,m,w<<);
if(b>m) update(a,b,d,m+,r,w<<|);
up(w);
}
int find(int k,int l,int r,int w)
{
// cout<<s[w]<<" "<<tm[w]<<" "<<l<<" "<<r<<" "<<w<<endl;
if(l==r)
{
//cout<<l<<" "<<tm[w]<<endl;
return l;
}
int m = (l+r)>>;
down(w,r-l+);
if(tm[w<<]>k)
return find(k,l,m,w<<);
else
return find(k,m+,r,w<<|);
}
LL query(int a,int b,int l,int r,int w)
{
if(a<=l&&b>=r)
{
return s[w];
}
int m = (l+r)>>;
LL res = ;
down(w,r-l+);
if(a<=m)
res+=query(a,b,l,m,w<<);
if(b>m)
res+=query(a,b,m+,r,w<<|);
return res;
}
int main()
{
int n,i;
while(scanf("%d",&n)&&n)
{
f.clear();
memset(p,,sizeof(p));
memset(vis,,sizeof(vis));
memset(lz,-,sizeof(lz));
for(i = ; i <= n ; i++)
po[i].clear();
int g = ;
for(i = ; i <= n; i++)
{
scanf("%d",&a[i]);
if(!f[a[i]]) f[a[i]] = ++g;
po[f[a[i]]].push_back(i);
}
int o = ;
for(i = ; i <= n ; i++)
{
if(a[i]<N)
vis[a[i]] = ;
if(a[i]<sum[i-])
sum[i] = sum[i-];
else
{
while(vis[o])
o++;
sum[i] = o;
}
}
LL ans=;
build(,n,);
ans+=s[];
//cout<<ans<<endl;
p[f[a[]]] = ;
for(i = ; i <= n ; i++)
{
int k;
int mk = f[a[i-]];
if(p[mk]<po[mk].size())
{
k = po[mk][p[mk]]-;
//cout<<k<<endl;
//p[mk]++;
}
else k = n;
update(,i-,,,n,);
int fk;
if(tm[]>a[i-])
{
fk = find(a[i-],,n,);
update(fk,k,a[i-],,n,);
}
ans+=query(i,n,,n,);
// cout<<ans<<endl;
//cout<<ans<<" "<<fk<<" "<<k<<" "<<a[i-1]<<endl;
//cout<<i<<endl;
//if(a[i]!=a[i-1])
p[f[a[i]]]++;
}
cout<<ans<<endl;
}
return ;
}

Mex(线段树的巧妙应用)的更多相关文章

  1. HDU-4747 Mex 线段树

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4747 题意:求一个数列中,所有mex(L,R)的和. 注意到mex是单调不降的,那么首先预处理出mex ...

  2. [置顶] hdu4747 Mex 线段树

    题意:给你一个序列,让你求出对于所有区间<i, j>的mex和,mex表示该区间没有出现过的最小的整数. 思路:从时限和点数就可以看出是线段树,并且我们可以枚举左端点i, 然后求出所有左端 ...

  3. 【插队问题-线段树-思维巧妙】【poj2828】Buy Tickets

    可耻的看了题解 巧妙的思维 逆序插入,pos 代表的意义为前面要有pos个空格才OK: 证明:仔细思考一下就觉得是正确的,但是要想到这种方式还是要很聪明,空格是前面的几个数字所形成的,所以要特地留出来 ...

  4. BZOJ.3585.mex(线段树)

    题目链接 题意:多次求区间\(mex\). 考虑\([1,i]\)的\(mex[i]\),显然是单调的 而对于\([l,r]\)与\([l+1,r]\),如果\(nxt[a[l]]>r\),那么 ...

  5. hdu 4747 mex 线段树+思维

    http://acm.hdu.edu.cn/showproblem.php?pid=4747 题意: 我们定义mex(l,r)表示一个序列a[l]....a[r]中没有出现过得最小的非负整数, 然后我 ...

  6. bzoj 3585 mex - 线段树 - 分块 - 莫队算法

    Description 有一个长度为n的数组{a1,a2,...,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. Input 第一行n,m. 第二行为n个数. 从第三行开始,每行一个询问 ...

  7. Codeforces 1083C Max Mex [线段树]

    洛谷 Codeforces 思路 很容易发现答案满足单调性,可以二分答案. 接下来询问就转换成判断前缀点集是否能组成一条链. 我最初的想法:找到点集的直径,判断直径是否覆盖了所有点,需要用到树套树,复 ...

  8. 【bzoj3585】mex 线段树 mex,sg

    Description 有一个长度为n的数组{a1,a2,…,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. Input 第一行n,m. 第二行为n个数. 从第三行开始,每行一个询问l, ...

  9. B - Bash and a Tough Math Puzzle CodeForces - 914D (线段树的巧妙应用)

    题目大意:当输入2时,将p处的点的值修改为x, 当输入1时,判断区间[L,R]的gcd是否几乎正确,几乎正确的定义是最多修改一个数,使得区间[L,R]的gcd为x. 题解:用线段树维护一个gcd数组, ...

随机推荐

  1. PHP 导出office打开乱码

    Response.AddHeader("Content-Disposition", "attachment; filename=" + file.Name); ...

  2. nyoj 86 --位标记

    nyoj 86 --位标记 点击打开题目链接 :                        找球号(一)  这道题目很多解法,其他解法请参考 http://www.cnblogs.com/play ...

  3. CISCO-端口安全

    1.MAC地址与端口绑定,当发现主机的MAC地址与交换机上指定的MAC地址不同时 ,交换机相应的端口将down掉.当给端口指定MAC地址时,端口模式必须为access或者Trunk状况. 3550-1 ...

  4. Linux下抓包工具tcpdump应用详解

      TCPDUMP简介 在传统的网络分析和测试技术中,嗅探器(sniffer)是最常见,也是最重要的技术之一.sniffer工具首先是为网络管理员和网络程序员进行网络分析而设计的.对于网络管理人员来说 ...

  5. SPOJ:D-query(非常规主席树求区间不同数的个数)

    Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query is a pair (i, j) ...

  6. BZOJ_2002_[Hnoi2010]Bounce 弹飞绵羊_LCT

    BZOJ_2002_[Hnoi2010]Bounce 弹飞绵羊_LCT Description 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏. ...

  7. PYTHON 异常处理 一 ASSERT

    assert语句,如果没记错,这个东西在C或者C++里面也有的.属于短小的断言.下面的是来自python help document的说明: Assert statements are a conve ...

  8. Mysql错误: ERROR 1205: Lock wait timeout exceeded; try restarting transaction

    MySQL:innodb的事务锁,一个线程占用着,简单做法是:执行mysql命令: show full processlist; 然后找出查询语句的系统id:kill掉被锁住的线程id:kill 12 ...

  9. Nhibernate中多Or条件的查询,很多Or的查询

    public IList<object[]> GetRequestAllByUserCodeUnitSysClassify1(string unitNo, string system, s ...

  10. Yet Another Number Sequence

    题意: $F_0 = 0, F_1 = 1, F_n = F_{n-1} + F_{n-2}$ 求解$\sum_{i=1}^n{ F_i i^K } \  mod \  10^9+7$. 解法: 记$ ...