[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. ES6系列_6之新增的数组知识

    1.JSON数组格式转换 JSON的数组格式就是为了前端快速的把JSON转换成数组的一种格式,json数组格式如下: let json = { '0': '男', '1': '女', length:3 ...

  2. 关于hibernate4.3版本之后org.hibernate.service.ServiceRegistryBuilder被弃用

    之前一直都是使用hibernate4.2.21的我,有一天突然没有使用本地的jar包而是让IDEA自动下载最新版本的hibernate5.2.2之后,发现有几个经常使用的方法报错了. -这真是让我惊了 ...

  3. c# 后台调用接口接收传过来的json

    public string GetRequestTest(string url) { HttpWebRequest httpWebRequest = (HttpWebRequest)WebReques ...

  4. mysql 删除死锁的事务

    select * from information_schema.innodb_trxkill 953

  5. spring集成mybatis配置多个数据源,通过aop自动切换

    spring集成mybatis,配置多个数据源并自动切换. spring-mybatis.xml如下: <?xml version="1.0" encoding=" ...

  6. linux sudo 系统环境变量 用户环境变量

    1. sudo就是普通用户临时拥有root的权限.好处在于,大多数时候使用用户自定义的配置,少数情况可以通过sudo实现root权限做事. 故而,需要注意的一点是,在你使用了sudo后,你临时不再是原 ...

  7. 第一个独特字符位置 · first position unique character

    [抄题]: 给出一个字符串.找到字符串中第一个不重复的字符然后返回它的下标.如果不存在这样的字符,返回 -1. 给出字符串 s = "lintcode",返回 0.给出字符串 s ...

  8. Qt Read and Write Csv File

    This page discusses various available options for working with csv documents in your Qt application. ...

  9. Brophp Nginx 虚拟主机的配置

    默认情况下,Nginx 不支持 pathinfo 配置,通过查看了 Thinkphp Nginx 的配置情况,对虚拟主机配置修改如下: server {     listen 80;     list ...

  10. [Selenium] 在Grid模式下打印出当前Case是在哪台Node上运行

    AAAbstractFlow() public void getComputerNameOfNode(WebDriver driver){ String CIHub = Environment.get ...