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数组, ...
随机推荐
- DB2删除表分区
近日,由于部门数据库读库空间过小,提出删除掉两个月之前日志表的分区(数据库分区是按时间月分区),记述如下: 上网搜索资料发现删除表分区大概分这么几步: 1.查询需要删除掉的分区: select t.D ...
- BZOJ_2716_[Violet 3]天使玩偶&&BZOJ_2648_SJY摆棋子_KDTree
BZOJ_2716_[Violet 3]天使玩偶&&BZOJ_2648_SJY摆棋子_KDTree Description 这天,SJY显得无聊.在家自己玩.在一个棋盘上,有N个黑色棋 ...
- Tensorflow基础知识
基本知识 使用 TensorFlow, 你必须明白 TensorFlow: 使用图 (graph) 来表示计算任务. 在被称之为 会话 (Session) 的上下文 (context) 中执行图. 使 ...
- GitHub的使用方法
版本控制系统 > Git 分布式 > Subversion 集中式 1. 安装git: # apt-get install git //root权限 $ sudo apt-get inst ...
- bzoj 3745 [Coci2015]Norma——序列分治
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3745 如果分治,就能在本层仅算过 mid 的区间了. 可以从中间到左边地遍历左边,给右边两个 ...
- vue-touchjs
支持vue2.0的面向指令的touch指令,基于touchjs(原百度实现的移动端手势库) vue-touchjs支持三种stopPropagation的方式: 1 .stop修饰符 2 事件han ...
- PICO 中关于时基ps3000aGetTimebase函数介绍
- Lombok减少代码冗余量
Eclipse需要安装,具体用法见: https://projectlombok.org/ 用maven project的朋友,一定要记得安装到IED里面才能使用,不然无法直接使用哦 从此以后和get ...
- Math对象产生随机数一个小应用
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- Lightoj1093 【线段树】
题意: 给出n个数,然后对于D区间的数求一个最大差值 思路: 区间最大最小...我居然没想到线段树... #include <bits/stdc++.h> using namespace ...