[JXOI2017]颜色 线段树扫描线 + 单调栈
题解:
首先题目要求删除一些颜色,换个说法就是要求保留一些颜色,那么观察到,如果我们设ll[i]和rr[i]分别表示颜色i出现的最左边的那个点和最右边的那个点,那么题目就是在要求我们选出的区间要满足区间[l, r]内所有颜色的max(rr[i]) <= r,并且min(ll[i]) >= l. 因为是区间相关的问题,又涉及到左右端点,因此我们考虑扫描线,那么考虑如何维护它。
因为每个颜色的ll[i]和rr[i]可以看做构成了一个区间,那么现在已经进入线段树的节点就分2种情况。
1,区间右端点超过当前右端点:
    
我们找到离当前右端点最近的点x,使得它代表的区间和右端点关系如上图所示,那么显然这个点x以及它之前的左端点都是不能取的,又因为这个点x是离当前右端点最近的满足条件的点,所以这个点之后都不会因为这个条件而产生冲突,即在这个点后面的,在当前右端点前面的,都满足了右端点的限制。那么我们只需要再满足左端点的限制,然后查询(x, i)对答案的贡献,其中i是当前枚举的右端点。那么我们如何找这个点呢?
观察到一个性质,在后面出现的点的右端点>= 前面出现的点的右端点 的情况下,在后面出现的肯定会更优;因此我们只需要维护一个右端点单调递减的单调栈即可,如果有一个右端点更右出现了,那么肯定会比之前右端点比它小(相等)的点更优,但是不能弹掉右端点比它大的,因为随着右端点的增大,可能这个点就失效了,但之前右端点比它大的点还是有效的。
2,区间右端点不超过当前右端点。
    
对于这种情况而言,显然我们要么把这个区间全部取了,要么一点都不取。因此不合法的左端点就是(ll, rr],把这段赋0即可。观察到因为我们是赋0,不是-1,所以无法撤销,但是这是没有关系的,因此如果在当前右端点下,这个区间已经不超过它了,那么以后随着右端点的增大,就更不可能超过了,因此不需要撤回。同时也正是因为无法撤回,所以上面那种情况需要单调栈而不是直接修改,因为上面那种情况,随着右端点的增大,是会变成第二种情况的。
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 301000
#define ac 1200100
#define LL long long /*用栈来维护查询的区间(因为每次的区间不同,而且都只要修改前缀,所以完全不用每次修改
(修改了就无法转移到下一个右端点了,因为不满足区间减法,无法撤销),
只需要查询指定区间内的就可以了*/ int n, tot, T;
LL ans;
int ll[AC], rr[AC], color[AC];
int s[AC], top;//栈
int tree[ac], lazy[ac], l[ac], r[ac];//线段树
struct co{
int color, id;
}p[AC]; struct seg{
int l, r;
}line[AC]; bool z[AC]; inline int read()
{
int x = ;char c = getchar();
while(c > '' || c < '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x;
} inline bool cmp1(seg a, seg b)
{
return a.r < b.r;
} inline bool cmp(co a, co b)
{
if(a.color != b.color) return a.color < b.color;
else return a.id < b.id;
} inline void pushdown(int x)
{
if(l[x] != r[x] && lazy[x])
{
int ll = x * , rr = ll + ;
tree[ll] = tree[rr] = lazy[x] = ;
lazy[ll] = lazy[rr] = ;
}
} inline void update(int x)
{
tree[x] = tree[x * ] + tree[x * + ];
} void build(int x, int ll, int rr)
{
l[x] = ll, r[x] = rr, lazy[x] = ;
if(ll == rr) {tree[x] = ; return ;}
int mid = (ll + rr) >> ;
build(x * , ll, mid), build(x * + , mid + , rr);
update(x);
} void change(int x, int ll, int rr)
{
pushdown(x);
if(l[x] == ll && r[x] == rr) {tree[x] = , lazy[x] = ; return ;}
int mid = (l[x] + r[x]) >> ;
if(rr <= mid) change(x * , ll, rr);
else if(ll > mid) change(x * + , ll, rr);
else change(x * , ll, mid), change(x * + , mid + , rr);
update(x);
} void find(int x, int ll, int rr)
{
pushdown(x);
if(l[x] == ll && r[x] == rr){ans += tree[x]; return ;}
int mid = (l[x] + r[x]) >> ;
if(rr <= mid) find(x * , ll, rr);
else if(ll > mid) find(x * + , ll, rr);
else find(x * , ll, mid), find(x * + , mid + , rr);
update(x);
} void pre()
{
n = read();
for(R i = ; i <= n; i ++) color[i] = p[i].color = read(), p[i].id = i;
sort(p + , p + n + , cmp);
for(R i = ; i <= n; i ++)
if(p[i].color != p[i - ].color)
rr[p[i - ].color] = p[i - ].id, ll[p[i].color] = p[i].id;
rr[p[n].color] = p[n].id;
for(R i = ; i <= n; i ++)
if(ll[i]) line[++tot] = (seg){ll[i], rr[i]};
sort(line + , line + tot + , cmp1);
} void init()
{
memset(ll, , sizeof(ll)), memset(rr, , sizeof(rr));
tot = ans = top = ;
} void get()
{
int l = ;
for(R i = ; i <= n; i ++)//不断扩大右端点
{
//printf("!!!%d\n", i);
while(top && rr[color[i]] >= rr[color[s[top]]]) -- top;//如果一个点在栈顶右侧,且右端点大于等于栈顶,那么它肯定更优。
s[++top] = i;//栈里面存颜色就够了, ,,,不,,,还是需要存下标
while(top && rr[color[s[top]]] <= i) -- top;//去掉不合法的情况
for(; line[l].r <= i && l <= tot; ++ l)//error!!!这里要用tot,不然的话用n可能会用到一些未被覆盖的,来自前面的数据的区间
if(line[l].l < line[l].r) change(, line[l].l + , line[l].r);
if(s[top] + <= i) find(, s[top] + , i); //要有合法的情况才查询,否则没有必要查询
}
printf("%lld\n", ans);
} void work()
{
T = read();
while(T --) init(), pre(), build(, , n), get();
} int main()
{
// freopen("color7.in", "r", stdin);
work();
// fclose(stdin);
return ;
}
[JXOI2017]颜色 线段树扫描线 + 单调栈的更多相关文章
- [CSP-S模拟测试]:陶陶摘苹果(线段树维护单调栈)
		题目传送门(内部题116) 输入格式 第一行两个整数$n,m$,如题 第二行有$n$个整数表示$h_1-h_n(1\leqslant h_i\leqslant 10^9)$ 接下来有$m$行,每行两个 ... 
- Wannafly挑战赛18 E 极差(线段树、单调栈)
		Wannafly挑战赛18 E 极差 题意 给出三个长度为n的正整数序列,一个区间[L,R]的价值定义为:三个序列中,这个区间的极差(最大值与最小值之差)的乘积. 求所有区间的价值之和.答案对\(2^ ... 
- HDU 5875 Function (线段树+gcd / 单调栈)
		题意:给你一串数a再给你一些区间(lef,rig),求出a[lef]%a[lef+1]...%a[rig] 题解:我们可以发现数字a对数字b取模时:如果a<b,则等于原数,否则a会变小至少一半. ... 
- Educational Codeforces Round 61 (Rated for Div. 2) G(线段树,单调栈)
		#include<bits/stdc++.h>using namespace std;int st[1000007];int top;int s[1000007],t[1000007];i ... 
- [BZOJ 2957]楼房重建(THU2013集训)(线段树维护单调栈)
		题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2957 分析: 根据题意,就是比较斜率大小 只看一段区间的话,那么这段区间能看见的楼房数量就是这 ... 
- 洛谷P4065 [JXOI2017]颜色(线段树)
		题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ... 
- 洛谷 P4198 楼房重建 线段树维护单调栈
		P4198 楼房重建 题目链接 https://www.luogu.org/problemnew/show/P4198 题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上 ... 
- [CSP-S模拟测试]:God Knows(线段树维护单调栈)
		题目描述 小$w$来到天堂的门口,对着天堂的大门发呆.大门上有一个二分图,左边第$i$个点连到右边第$p_i$个点.(保证$p_i$是一个排列).小$w$每次可以找左边某个对应连线尚未被移除的点$i$ ... 
- 洛谷P3246 序列 [HNOI2016] 莫队/线段树+扫描线
		正解:莫队/线段树+扫描线 解题报告: 传送门! 似乎是有两种方法的,,,所以分别港下好了QAQ 第一种,莫队 看到这种询问很多区间之类的就会自然而然地想到莫队趴?然后仔细思考一下,发现复杂度似乎是欧 ... 
随机推荐
- EF SQLite的Like语句,生成为CHARINDEX的解决办法
			在使用EF SQLite的时候发现Like语句不能完全查询出来,看了下生成的SQL语句类似于这种 (CHARINDEX(@p__linq__2, [Extent1].[LeagueName])) &g ... 
- Python  列表下标操作
			Python 列表下标操作 引用网址: https://www.jianshu.com/p/a98e935e4d46 
- PHP使用Redis消息队列
			1.redis安装 参考:菜鸟教程http://www.runoob.com/redis/redis-install.html 2.安装php的redis扩展 1)phpinfo()查看php版本信息 ... 
- XSS----payload,绕过,xss小游戏记录
			一.XSS 1.原理:攻击者把恶意的脚本代码注入到网页中,等待其他用户浏览 这些网页(或触发其他条件),从而执行其中的恶意代码. 1.xss实例代码: test.html <!DOCTYPE h ... 
- <cfenv>(fenv.h) _c++11
			头文件 <cfenv>(fenv.h) c++11 浮点环境 这个头文件声明了一系列的函数和宏去访问浮点环境,以及特殊的类型. 浮点环境维护一系列的状态标志(status flags)和具 ... 
- MyBatis 注解配置及动态SQL
			一.注解配置 目前MyBatis支持注解配置,用注解方式来替代映射文件,但是注解配置还是有点不完善,在开发中使用比较少,大部分的企业还是在用映射文件来进行配置.不完善的地方体现在于当数据表中的字段 ... 
- priority_queue(优先队列):排序不去重
			C++优先队列类似队列,但是在这个数据结构中的元素按照一定的断言排列有序. 头文件:#include<queue> 参数:priority_queue<Type, Container ... 
- 使用eclipse创建maven项目出现的一个问题
			错误信息 This error occurs when you employ a plugin that Maven could not download. Possible causes for t ... 
- Hadoop,MapReduce操作Mysql
			前以前帖子介绍,怎样读取文本数据源和多个数据源的合并:http://www.cnblogs.com/liqizhou/archive/2012/05/15/2501835.html 这一个博客介绍一下 ... 
- javascript打开新窗口
			一.window.open()支持环境: JavaScript1.0+/JScript1.0+/Nav2+/IE3+/Opera3+ 二.基本语法: window.open(pageURL,name, ... 
