Mex(线段树的巧妙应用)
题目要求求某段区间第一个没有出现的数(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(线段树的巧妙应用)的更多相关文章
- HDU-4747 Mex 线段树
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4747 题意:求一个数列中,所有mex(L,R)的和. 注意到mex是单调不降的,那么首先预处理出mex ...
- [置顶] hdu4747 Mex 线段树
题意:给你一个序列,让你求出对于所有区间<i, j>的mex和,mex表示该区间没有出现过的最小的整数. 思路:从时限和点数就可以看出是线段树,并且我们可以枚举左端点i, 然后求出所有左端 ...
- 【插队问题-线段树-思维巧妙】【poj2828】Buy Tickets
可耻的看了题解 巧妙的思维 逆序插入,pos 代表的意义为前面要有pos个空格才OK: 证明:仔细思考一下就觉得是正确的,但是要想到这种方式还是要很聪明,空格是前面的几个数字所形成的,所以要特地留出来 ...
- BZOJ.3585.mex(线段树)
题目链接 题意:多次求区间\(mex\). 考虑\([1,i]\)的\(mex[i]\),显然是单调的 而对于\([l,r]\)与\([l+1,r]\),如果\(nxt[a[l]]>r\),那么 ...
- hdu 4747 mex 线段树+思维
http://acm.hdu.edu.cn/showproblem.php?pid=4747 题意: 我们定义mex(l,r)表示一个序列a[l]....a[r]中没有出现过得最小的非负整数, 然后我 ...
- bzoj 3585 mex - 线段树 - 分块 - 莫队算法
Description 有一个长度为n的数组{a1,a2,...,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. Input 第一行n,m. 第二行为n个数. 从第三行开始,每行一个询问 ...
- Codeforces 1083C Max Mex [线段树]
洛谷 Codeforces 思路 很容易发现答案满足单调性,可以二分答案. 接下来询问就转换成判断前缀点集是否能组成一条链. 我最初的想法:找到点集的直径,判断直径是否覆盖了所有点,需要用到树套树,复 ...
- 【bzoj3585】mex 线段树 mex,sg
Description 有一个长度为n的数组{a1,a2,…,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. Input 第一行n,m. 第二行为n个数. 从第三行开始,每行一个询问l, ...
- B - Bash and a Tough Math Puzzle CodeForces - 914D (线段树的巧妙应用)
题目大意:当输入2时,将p处的点的值修改为x, 当输入1时,判断区间[L,R]的gcd是否几乎正确,几乎正确的定义是最多修改一个数,使得区间[L,R]的gcd为x. 题解:用线段树维护一个gcd数组, ...
随机推荐
- tomcat启动项目被重新加载,导致资源初始化两遍
之前没有遇到过这个问题,配了三天的项目了,惊人啊!!!各种怪问题全被我赶上了.真有种骂人的冲动. tomcat启动项目时,项目资源被加载两遍. 原因:配置虚拟目录导致,项目被重新加载. <Hos ...
- HTML标签深入学习系列(1)——注释标签 <!-- -->
一.HTML注释的语法 <!--注释内容--> 二.HTML注释的用处 1.普通注释(增强代码的可读性) 方便别人:方便其它程序员了解你的代码 方便自己:方便以后对自己代码的理解与修改等等 ...
- 多线程之:lock和synchronized的区别
多次思考过这个问题,都没有形成理论,今天有时间了,我把他总结出来,希望对大家有所帮助 1.ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候 ...
- ab-如何提交post请求
ab -n 1 -c 1 -p post.txt http://***********/ 文件post.txt中存放要post的数据
- bzoj 2238 Mst——树链剖分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2238 一条非树边可以对一条链的树边产生影响.注意是边,所以把边下放到点上,只要跳 top 时 ...
- ubuntu16.04 NVIDIA 驱动安装
查看驱动版本号 查看驱动适用版本:NVIDIA驱动版本查询 查看显卡对应的驱动版本: 举例如下: 禁止集成的nouveau驱动 Ubuntu系统集成的显卡驱动程序是nouveau,它是第三方为NVID ...
- moco实例
一.moco模拟接口响应json moco的下载地址见虫师博客园:https://www.cnblogs.com/fnng/p/7511539.html foo.json文件内容如下 [ { &quo ...
- 准备看的ros相关源码
进程通信:lcm 导航:navigation 3D可视化工具:rviz Mobile Robot Programming Toolkit:mrpt 其他: 人体肌肉:simbody openslam ...
- Java 类加载器的作用
深入探讨 Java 类加载器 成 富, 软件工程师, IBM 中国软件开发中心 成富任职于 IBM 中国软件开发中心,目前在 Lotus 部门从事 IBM Mashup Center 的开发工作.他毕 ...
- 在ANGULAR6中使用Echarts的正确方式之一
这里的正确指的是不会在运行过程中报错,不会再prod模式下编译报错,不会再AOT模式下编译报错 个人环境说明: { "name": "angular-for-echart ...