[JXOI2017]颜色

题目链接

https://www.luogu.org/problemnew/show/P4065

题目描述

可怜有一个长度为 n 的正整数序列 Ai,其中相同的正整数代表着相同的颜色。

现在可怜觉得这个序列太长了,于是她决定选择一些颜色把这些颜色的所有位置都删去。

删除颜色 i 可以定义为把所有满足 Aj = i 的位置 j 都从序列中删去。

然而有些时候删去之后,整个序列变成了好几段,可怜不喜欢这样,于是她想要知道有多少种删去颜色的方案使得最后剩下来的序列非空且连续。

例如颜色序列 {1, 2, 3, 4, 5},删除颜色 3 后序列变成了 {1, 2} 和 {4, 5} 两段,不满足条件。而删除颜色 1 后序列变成了 {2, 3, 4, 5},满足条件。

两个方案不同当且仅当至少存在一个颜色 i 只在其中一个方案中被删去。

输入输出格式

输入格式:

第一行输入一个整数 T 表示数据组数。每组数据第一行输入一个整数 n 表示数列长度。第二行输入 n 个整数描述颜色序列。

输出格式:

对于每组数据输出一个整数表示答案。

输入输出样例

输入样例#1:
复制

1
5
1 3 2 4 3
输出样例#1: 复制

6

说明

满足条件的删颜色方案有 {1}, {1, 3}, {1, 2, 3}, {1, 3, 4}, {2, 3, 4}, ∅.

对于 20% 的数据,保证 1 ≤∑n ≤ 20。

对于 40% 的数据,保证 1 ≤∑n ≤ 500。

对于 60% 的数据,保证 1 ≤∑n ≤ 10^4。

对于 100% 的数据,保证 1 ≤ T,∑n ≤ 3 × 10^5, 1 ≤ Ai ≤ n。

题解

每段合法区间都是连续的,自然而然会想到枚举一个端点,然后求另一个端点的合法个数。

感觉这个套路比较常见,但是我依然不会。

我们可以先枚举右端点,然后怎么确定左端点的个数呢,感觉很麻烦是不是,正难则反嘛。

定义a[i]为i点的值,head[a[i]]为a[i]最左端点的位置,tail[i]为其最右端的位置。

枚举右端点,当右端点为第i位,令R=i,假设存在一个L,使得区间(L,R)为合法区间,

对于j(L<=j<=R),满足:

1.tail[a[j]]<=R

2.head[a[j]]>=L

到这一步接下来就有点难想到了。

对于每个点i,如果i=tail[a[i]],那么对于任何区间,head[a[i]]+1~tail[a[i]]不可能是左端点,我们将这段区间赋值成一。

再找到最靠近i的k满足,tail[k]>i,则(1,k)之间不可能存在左端点。

i为右端点的贡献等于,i-k-sum[k+1,i](sum[k+1,i]为k+1到i的被标记了的总和,蓝字部分).

至于怎么找k,可以用单调栈来维护,因为弹出栈的话,就不会再用到了。

代码

 #include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 500050 int n,top,a[N],sk[N];
int head[N],tail[N],last[N],nex[N];
struct Tree {int l,r,lazy,sum;}tr[N<<];
template<typename T>void read(T&x)
{
ll k=; char c=getchar();
x=;
while(!isdigit(c)&&c!=EOF)k^=c=='-',c=getchar();
if (c==EOF)exit();
while(isdigit(c))x=x*+c-'',c=getchar();
x=k?-x:x;
}
void read_char(char &c)
{while(!isalpha(c=getchar())&&c!=EOF);}
void push_up(int x)
{
int len=tr[x].r-tr[x].l+;
if (len>)tr[x].sum=tr[x<<].sum+tr[x<<|].sum;
else tr[x].sum=;
if (tr[x].lazy!=-)
tr[x].sum=tr[x].lazy*len;
}
void push_down(int x)
{
if (tr[x].lazy==-)return;
tr[x<<].lazy=tr[x].lazy;
tr[x<<|].lazy=tr[x].lazy;
push_up(x<<);
push_up(x<<|);
tr[x].lazy=-;
}
void bt(int x,int l,int r)
{
tr[x]=Tree{l,r,-,};
if (l==r) return ;
int mid=(l+r)>>;
bt(x<<,l,mid);
bt(x<<|,mid+,r);
}
void update(int x,int l,int r)
{
if (l>r)return;
if (l<=tr[x].l&&tr[x].r<=r)
{
tr[x].lazy=;
push_up(x);
return ;
}
int mid=(tr[x].l+tr[x].r)>>;
push_down(x);
if (l<=mid)update(x<<,l,r);
if (mid<r)update(x<<|,l,r);
push_up(x);
}
int query(int x,int l,int r)
{
if (l<=tr[x].l&&tr[x].r<=r)
return tr[x].sum;
int mid=(tr[x].l+tr[x].r)>>,ans=;
push_down(x);
if (l<=mid)ans=query(x<<,l,r);
if (mid<r)ans+=query(x<<|,l,r);
push_up(x);
return ans;
}
void work()
{
read(n);
for(int i=;i<=n;i++) read(a[i]);
for(int i=;i<=n;i++)
{
if (head[a[i]]==)head[a[i]]=i;
nex[tail[a[i]]]=i;
last[i]=tail[a[i]];
tail[a[i]]=i;
}
bt(,,n);
sk[top=]=;
tail[]=n+;
ll ans=;
for(int i=;i<=n;i++)
{
sk[++top]=i;
while(top&&tail[a[sk[top]]]<=i)top--;
int j=sk[top];
if (tail[a[i]]==i)
{
update(,head[a[i]]+,tail[a[i]]);
ans+=i-j-query(,j+,i);
}
}
printf("%lld\n",ans);
}
void clear()
{
memset(head,,sizeof(head));
memset(tail,,sizeof(tail));
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
int q;
read(q);
while(q--)
{
clear();
work();
}
}

[JXOI2017]颜色 线段树求点对贡献的更多相关文章

  1. [JXOI2017]颜色 线段树扫描线 + 单调栈

    ---题面--- 题解: 首先题目要求删除一些颜色,换个说法就是要求保留一些颜色,那么观察到,如果我们设ll[i]和rr[i]分别表示颜色i出现的最左边的那个点和最右边的那个点,那么题目就是在要求我们 ...

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

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

  3. 2016年湖南省第十二届大学生计算机程序设计竞赛---Parenthesis(线段树求区间最值)

    原题链接 http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1809 Description Bobo has a balanced parenthes ...

  4. UVA 11983 Weird Advertisement --线段树求矩形问题

    题意:给出n个矩形,求矩形中被覆盖K次以上的面积的和. 解法:整体与求矩形面积并差不多,不过在更新pushup改变len的时候,要有一层循环,来更新tree[rt].len[i],其中tree[rt] ...

  5. BNU 2418 Ultra-QuickSort (线段树求逆序对)

    题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=2418 解题报告:就是给你n个数,然后让你求这个数列的逆序对是多少?题目中n的范围是n & ...

  6. hdu 1394 (线段树求逆序数)

    <题目链接> 题意描述: 给你一个有0--n-1数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,那么这样就形成了n个序列,那么每个序列都有一个逆序数,找 ...

  7. xdoj-1324 (区间离散化-线段树求区间最值)

    思想 : 1 优化:题意是覆盖点,将区间看成 (l,r)转化为( l-1,r) 覆盖区间 2 核心:dp[i]  覆盖从1到i区间的最小花费 dp[a[i].r]=min (dp[k])+a[i]s; ...

  8. 4163 hzwer与逆序对 (codevs + 权值线段树 + 求逆序对)

    题目链接:http://codevs.cn/problem/4163/ 题目:

  9. poj2299 Ultra-QuickSort(线段树求逆序对)

    Description In this problem, you have to analyze a particular sorting algorithm. The algorithm proce ...

随机推荐

  1. upstream prematurely closed connection while reading response header from upstream

    upstream prematurely closed connection while reading response header from upstream nginx配置uwsgi的时候  ...

  2. JSR 303 - Bean Validation 简介及使用方法

    参考:https://blog.csdn.net/xlgen157387/article/details/46848507 自己写的验证: /** * * @ClassName: BeanValida ...

  3. Symfony 2.0 认识Request, Response, Session, Cookie

    在上一节中,我们提到了如何创建一个Bunlde 并且在默认控制器中添加一些方法.如果有参照之前的说法进行的话,读者很有可能会被提示说 返回的Response对象不能为空.好啦,我们就来研究一下,怎么从 ...

  4. manjaro 下golang protobuf的使用

    1.下载protobuf compiler sudo pacman -S protobuf 2.添加环境变量GOBIN export GOBIN=~/go/bin 3.下载golang依赖的包 go ...

  5. 输入输出参数 inout

    输入输出参数 inout 函数参数默认是常量.试图在函数体中更改参数值将会导致编译错误(compile-time error).这意味着你不能错误地更改参数值.如果你想要一个函数可以修改参数的值,并且 ...

  6. 洛谷 P2899 [USACO08JAN]手机网络Cell Phone Network(树形动规)

    题目描述 Farmer John has decided to give each of his cows a cell phone in hopes to encourage their socia ...

  7. 移动直播app怎么做

    今年移动直播行业的兴起,诞生了一大批网红,甚至明星也开始直播了,因此不得不跟上时代的步伐,教你从零开始搭建一个完整的iOS直播app,希望能帮助到更多的人更快的了解直播. 了解直播 1 热门直播产品: ...

  8. K8S中RC与Deployment的区别

    原文:http://fx114.net/qa-81-152379.aspx replication controller与deployment的区别 replication controller Re ...

  9. sql语句语句中的正则查找

    举例: select tncl_id from tncl where tncl_id regexp'^0065'; 有一表,数据有10万多条,其中某列数据示例如下: 100000-200000-300 ...

  10. swoole多进程操作

    多个任务同时执行 将顺序执行的任务,转化为并行执行(任务在逻辑上可以并行执行) 比如,我们要对已知的用户数据进行判断,是否需要发送邮件和短信,如果需要发送则发送. 不使用多进程时,我们首先判断是否发送 ...