~~~题面~~~

题解:

  首先题目要求删除一些颜色,换个说法就是要求保留一些颜色,那么观察到,如果我们设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]颜色 线段树扫描线 + 单调栈的更多相关文章

  1. [CSP-S模拟测试]:陶陶摘苹果(线段树维护单调栈)

    题目传送门(内部题116) 输入格式 第一行两个整数$n,m$,如题 第二行有$n$个整数表示$h_1-h_n(1\leqslant h_i\leqslant 10^9)$ 接下来有$m$行,每行两个 ...

  2. Wannafly挑战赛18 E 极差(线段树、单调栈)

    Wannafly挑战赛18 E 极差 题意 给出三个长度为n的正整数序列,一个区间[L,R]的价值定义为:三个序列中,这个区间的极差(最大值与最小值之差)的乘积. 求所有区间的价值之和.答案对\(2^ ...

  3. HDU 5875 Function (线段树+gcd / 单调栈)

    题意:给你一串数a再给你一些区间(lef,rig),求出a[lef]%a[lef+1]...%a[rig] 题解:我们可以发现数字a对数字b取模时:如果a<b,则等于原数,否则a会变小至少一半. ...

  4. 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 ...

  5. [BZOJ 2957]楼房重建(THU2013集训)(线段树维护单调栈)

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2957 分析: 根据题意,就是比较斜率大小 只看一段区间的话,那么这段区间能看见的楼房数量就是这 ...

  6. 洛谷P4065 [JXOI2017]颜色(线段树)

    题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ...

  7. 洛谷 P4198 楼房重建 线段树维护单调栈

    P4198 楼房重建 题目链接 https://www.luogu.org/problemnew/show/P4198 题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上 ...

  8. [CSP-S模拟测试]:God Knows(线段树维护单调栈)

    题目描述 小$w$来到天堂的门口,对着天堂的大门发呆.大门上有一个二分图,左边第$i$个点连到右边第$p_i$个点.(保证$p_i$是一个排列).小$w$每次可以找左边某个对应连线尚未被移除的点$i$ ...

  9. 洛谷P3246 序列 [HNOI2016] 莫队/线段树+扫描线

    正解:莫队/线段树+扫描线 解题报告: 传送门! 似乎是有两种方法的,,,所以分别港下好了QAQ 第一种,莫队 看到这种询问很多区间之类的就会自然而然地想到莫队趴?然后仔细思考一下,发现复杂度似乎是欧 ...

随机推荐

  1. Android AOSP 编译sdk

    首先你要有AOSP 工程. 然后执行下面的命令编译sdk. //运行下面的命令得到编译环境 source build/envsetup.sh lunch sdk make sdk 最后文件输出在哪个位 ...

  2. ThinkDev.Data更新日志

    2013-09-29 10:001.重构Where.And.Or.Having.JoinTable代码,新增条件组合查询QueryGroup2.1.1.2.0 2013-09-04 09:001.修复 ...

  3. RSA加密通信小结(二)-新版本APP与后台通信交互内容修改方案

    注1:本次修改分为两步,首先是内容相关的修改,待其完成之后,再进行加密通信项(粗体字备注)修改. 1.新的提交后台的格式包括:data,token(预留字段,暂时后台不校验),userId(已有的不删 ...

  4. 安装SQLSEVER与MySQL

    昨天装了一整填的SQLSEVER,今天是把昨天遗留的问题给重新整合一下,上午安装MySQL的时候,是在网上找的帖子通过压缩包安装的,捣鼓了一上午,下午花不到一个小时, 去安装好了,我觉得通过压缩包安装 ...

  5. Python递归算法入门

    递归是一种算法,它在编程里面有着广泛的应用: 1. 递归必须满足哪两个基本条件? 一.函数调用自身 二.设置了正确的返回条件 2. 思考一下,按照递归的特性,在编程中有没有不得不使用递归的情况? 答: ...

  6. 剑指offer-从上往下打印二叉树22

    题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印. class Solution: # 返回从上到下每个节点值列表,例:[1,2,3] def PrintFromTopToBottom( ...

  7. ArcFaceDemo 第二版【C#】——视频人脸识别

    啥话不说,不用跪求,直接给下载地址:http://common.tenzont.com/comdll/arcface2demo.zip(话说附件的大小不限制,还是说我的文件太大,实际上确实有点大,60 ...

  8. leetcode个人题解——#5 Container with most water

    class Solution { public: string longestPalindrome(string s) { int length = s.length(); ) return s; ; ...

  9. 特殊符号 & 以太坊

    &表示取二进制的末尾 &1表示如果末尾是奇数和偶数两种情况 0 偶数 1奇数 举例子: int a=1;int p=&a; 其中,p是指针,&a就是将a在内存中的实际地 ...

  10. 浅谈CPU、内存、硬盘之间的关系

    计算机,大家都知道的,就是我们日常用的电脑,不管台式的还是笔记本都是计算机.那么这个看着很复杂的机器由哪些组成的呢,今天就简单的来了解一下. 先放图: 图上展示的就是计算机的基本组成啦. 首先是输入设 ...