hdu 4747【线段树-成段更新】.cpp
题意:
给出一个有n个数的数列,并定义mex(l, r)表示数列中第l个元素到第r个元素中第一个没有出现的最小非负整数。
求出这个数列中所有mex的值。
思路:
可以看出对于一个数列,mex(r, r~l)是一个递增序列
mex(0, 0~n-1)是很好求的,只需要遍历找出第一个没有出现的最小非负整数就好了。这里有一个小技巧:
tmp = ;
for (int i = ; i <= n; ++i) {
mp[arr[i]] = ;
while (mp.find(tmp) != mp.end()) tmp++;
mex[i] = tmp;
}
这样可以利用map中的红黑树很快找到第一个没有出现的最小非负整数。
然后在求mex(1~n-1, 0~n-1)的过程中,我们可以看出,每消除当前值arr[i],会影响到的是在下一个arr[i]出现前 往后的mex值中比arr[i]大的值,即如果当前这个值不存在了,那么在这个值下一次出现前,mex值比当前值大的mex值都应被替换成arr[i]。
所以我们可以再一次利用map的红黑树找到当前值下一次出现的位置,然后利用线段树成段更新往后的mex值和求出会影响到的mex值的个数。
for (int i = n; i >= ; --i) {
if (mp.find(arr[i]) == mp.end()) next[i] = n+;
else next[i] = mp[arr[i]];
mp[arr[i]] = i;
}
这里我们还需要利用线段树求出第一个比当前arr[i]大的mex值的位置,以便成段更新区间的mex值。
Tips:
※ 这里有一个小小优化的地方,就是当更新的时候,可以先查看mx[1]是否比当前值大,如果是,则表示往后的区间里有比当前值大的mex值,则需要线段树是需要更新的,否则不用更新。
※ 还有一个要注意的地方是:只有求出的左边界比右边界小的时候才能更新。
Code:
#include <stdio.h>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std; const int MAXN = ;
long long sum[MAXN<<];
int mx[MAXN<<], arr[MAXN], next[MAXN], mex[MAXN];
int lazy[MAXN<<]; void Pushup(int rt)
{
sum[rt] = sum[rt<<]+sum[rt<<|];
mx[rt] = max(mx[rt<<], mx[rt<<|]);
} void Pushdown(int rt, int x)
{
if (lazy[rt] != -) {
lazy[rt<<] = lazy[rt<<|] = lazy[rt];
sum[rt<<] = (x-x/)*lazy[rt];
sum[rt<<|] = x/*lazy[rt];
mx[rt<<] = mx[rt<<|] = lazy[rt];
lazy[rt] = -;
}
} void Creat(int l, int r, int rt)
{
lazy[rt] = -;
if (l == r) {
sum[rt] = mx[rt] = mex[l];
return;
}
int mid = (l+r)/;
Creat(l, mid, rt<<);
Creat(mid+, r, rt<<|);
Pushup(rt);
} void Modify(int l, int r, int x, int L, int R, int rt)
{
if (l <= L && r >= R) {
lazy[rt] = x;
sum[rt] = x*(R-L+);
mx[rt] = x;
return;
}
Pushdown(rt, R-L+);
int mid = (L+R)/;
if (l <= mid) Modify(l, r, x, L, mid, rt<<);
if (r > mid) Modify(l, r, x, mid+, R, rt<<|);
Pushup(rt);
} int Get(int rt, int l, int r, int x)
{
if(l == r) return l;
Pushdown(rt, r-l+);
int mid = (l+r)/;
if (mx[rt<<] > x) return Get(rt<<, l, mid, x);
else return Get(rt<<|, mid+, r, x);
} int main()
{
//freopen("in.txt", "r", stdin);
int n, tmp;
long long ans_sum;
map<int, int> mp;
while (~scanf("%d", &n)) {
if (n == ) break;
ans_sum = tmp = ;
mp.clear();
memset(sum, , sizeof(sum));
memset(next, , sizeof(next)); for (int i = ; i <= n; ++i)
scanf("%d", &arr[i]);
for (int i = ; i <= n; ++i) {
mp[arr[i]] = ;
while (mp.find(tmp) != mp.end()) tmp++;
mex[i] = tmp;
} Creat(, n, );
mp.clear();
for (int i = n; i >= ; --i) {
if (mp.find(arr[i]) == mp.end()) next[i] = n+;
else next[i] = mp[arr[i]];
mp[arr[i]] = i;
} for (int i = ; i <= n; ++i) {
ans_sum += sum[];
if (mx[] > arr[i]) {
int l = Get(, , n, arr[i]);
int r = next[i];
// printf("%d %d %d\n", l, r, sum[1]);
if (l < r) Modify(l, r-, arr[i], , n, );
} Modify(i, i, , , n, );
}
printf("%I64d\n", ans_sum);
}
return ;
}
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4747
hdu 4747【线段树-成段更新】.cpp的更多相关文章
- hdu 1698 线段树 成段更新
题意:一段钩子,每个钩子的值为1,有若干更新,每次跟新某段的值,若干查询某段的和 基础题了 #include<cstdio> #include<iostream> #inclu ...
- hdu 1698 线段树成段更新
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1698 #include <cstdio> #include <cmath> # ...
- HDU 3577 Fast Arrangement ( 线段树 成段更新 区间最值 区间最大覆盖次数 )
线段树成段更新+区间最值. 注意某人的乘车区间是[a, b-1],因为他在b站就下车了. #include <cstdio> #include <cstring> #inclu ...
- ACM: Copying Data 线段树-成段更新-解题报告
Copying Data Time Limit:2000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Description W ...
- Codeforces Round #149 (Div. 2) E. XOR on Segment (线段树成段更新+二进制)
题目链接:http://codeforces.com/problemset/problem/242/E 给你n个数,m个操作,操作1是查询l到r之间的和,操作2是将l到r之间的每个数xor与x. 这题 ...
- POJ 2777 Count Color (线段树成段更新+二进制思维)
题目链接:http://poj.org/problem?id=2777 题意是有L个单位长的画板,T种颜色,O个操作.画板初始化为颜色1.操作C讲l到r单位之间的颜色变为c,操作P查询l到r单位之间的 ...
- HDU1698_Just a Hook(线段树/成段更新)
解题报告 题意: 原本区间1到n都是1,区间成段改变成一个值,求最后区间1到n的和. 思路: 线段树成段更新,区间去和. #include <iostream> #include < ...
- poj 3468 A Simple Problem with Integers 【线段树-成段更新】
题目:id=3468" target="_blank">poj 3468 A Simple Problem with Integers 题意:给出n个数.两种操作 ...
- POJ3468_A Simple Problem with Integers(线段树/成段更新)
解题报告 题意: 略 思路: 线段树成段更新,区间求和. #include <iostream> #include <cstring> #include <cstdio& ...
随机推荐
- Qt递归拷贝和删除目录
最近在翻看项目代码时,看到了这两个函数,想到这个功能十分常用,因此拿出来与大家分享,希望对大家有用.几点说明: 1.记得当初写代码那会,是参考了网上的帖子写的,做了一点小修改.因此代码源于网络. 2. ...
- VxWorks6.6 pcPentium BSP 使用说明(二):创建启动盘
本篇介绍从Solaris.Linux.Windows或VxWorks创建VxWorks启动盘的方法. 从Solaris或Linux创建启动盘 使用Solaris或Linux自带的工具/usr/bin/ ...
- [置顶] 单键模式的C++描述
设计模式-单键(Signelton):其实单键的设计模式说来很简单,说的直白一点就是程序运行过程中保证只有一个实例在运行而已.在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例, ...
- JDBC使用数据库来完成分页功能
本篇讲诉如何在页面中通过操作数据库来完成数据显示的分页功能.当一个操作数据库进行查询的语句返回的结果集内容如果过多,那么内存极有可能溢出,所以在大数据的情况下分页是必须的.当然分页能通过很多种方式来实 ...
- mysql高可用架构方案之二(keepalived+lvs+读写分离+负载均衡)
mysql主从复制与lvs+keepalived实现负载高可用 文件夹 1.前言 4 2.原理 4 2.1.概要介绍 4 2.2.工作原理 4 2.3.实际作用 4 3方 ...
- HDU 2098 分拆素数和(素数)
HDU 2098 分拆素数和(素数) http://acm.hdu.edu.cn/showproblem.php?pid=2098 题意: 给你一个偶数,问你这个偶数有多少种方式能由两个不同的素数构成 ...
- MFC程序的消息处理顺序
MFC应用程序中处理消息的顺序 1.AfxWndProc() 该函数负责接收消息,找到消息所属的CWnd对象,然后调用AfxCallWndProc 2.AfxCallWndProc() 该 ...
- Android编程心得-Handler与子线程的交互初步
在编写项目的时候,本人发现一个关于线程与Handler很容易犯的错误. 我有两个Activity,一个Activity在后台创建了一个线程并且启动,这个线程对象对应的实体实在另外一个Activity的 ...
- Tokyo Tyrant(TTServer)系列(四)-tcrmgr远程管理与调试
Tokyo Tyrant(TTServer)系列-tcrmgr(远程管理与调试) tcrmgr是TokyoTyrant的管理工具,对ttserver进行管理与执行命令: 通过输入tcrmgr回车,能够 ...
- ExtJs4 笔记(7) Ext.tip.ToolTip 提示
本篇介绍提示控件,ExtJs支持两种方式定义提示,可以支持普通html元素和一般的ExtJs UI控件. 一.基本提示 Ext.tip.ToolTip 1.最简单的提示 下面通过代码定义一个最简单的提 ...