[JXOI2017]颜色 线段树求点对贡献
[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, 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]颜色 线段树求点对贡献的更多相关文章
- [JXOI2017]颜色 线段树扫描线 + 单调栈
---题面--- 题解: 首先题目要求删除一些颜色,换个说法就是要求保留一些颜色,那么观察到,如果我们设ll[i]和rr[i]分别表示颜色i出现的最左边的那个点和最右边的那个点,那么题目就是在要求我们 ...
- 洛谷P4065 [JXOI2017]颜色(线段树)
题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ...
- 2016年湖南省第十二届大学生计算机程序设计竞赛---Parenthesis(线段树求区间最值)
原题链接 http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1809 Description Bobo has a balanced parenthes ...
- UVA 11983 Weird Advertisement --线段树求矩形问题
题意:给出n个矩形,求矩形中被覆盖K次以上的面积的和. 解法:整体与求矩形面积并差不多,不过在更新pushup改变len的时候,要有一层循环,来更新tree[rt].len[i],其中tree[rt] ...
- BNU 2418 Ultra-QuickSort (线段树求逆序对)
题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=2418 解题报告:就是给你n个数,然后让你求这个数列的逆序对是多少?题目中n的范围是n & ...
- hdu 1394 (线段树求逆序数)
<题目链接> 题意描述: 给你一个有0--n-1数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,那么这样就形成了n个序列,那么每个序列都有一个逆序数,找 ...
- xdoj-1324 (区间离散化-线段树求区间最值)
思想 : 1 优化:题意是覆盖点,将区间看成 (l,r)转化为( l-1,r) 覆盖区间 2 核心:dp[i] 覆盖从1到i区间的最小花费 dp[a[i].r]=min (dp[k])+a[i]s; ...
- 4163 hzwer与逆序对 (codevs + 权值线段树 + 求逆序对)
题目链接:http://codevs.cn/problem/4163/ 题目:
- poj2299 Ultra-QuickSort(线段树求逆序对)
Description In this problem, you have to analyze a particular sorting algorithm. The algorithm proce ...
随机推荐
- RAD XE8
http://community.embarcadero.com/index.php/blogs/entry/rad-studio-2015-roadmap http://www.embarcader ...
- java中getAttribute和getParameter的区别
getAttribute表示从request范围取得设置的属性,必须要先setAttribute设置属性,才能通过getAttribute来取得,设置与取得的为Object对象类型 getParame ...
- 8.3.2018 1 Quick and dirty 快而脏的快餐
Quick and dirty 快而脏的快餐 BEIJING 北京 Food delivery is a booming business. Waste is piling up, too 送餐 ...
- Go and Beego Development
1. Beego wiki in en and cn https://beego.me/ For API development: https://beego.me/blog/beego_api 2. ...
- 2017面向对象程序设计(Java)第十五周学习总结
上周,老师要求同学们自学应用程序部署,并布置了相关的实验任务.此次实验的目的是掌握Java应用程序的打包操作:了解应用程序存储配置信息的两种方法: 了解Applet小应用程序的开发及应用方法:掌握基于 ...
- Java Thread系列(五)synchronized
Java Thread系列(五)synchronized synchronized锁重入 关键字 synchronized 拥有锁重入的功能,也就是在使用 synchronized 时,当线程等到一个 ...
- IP转换成域名
DNS就是域名解析系统,它可以将IP转换成域名,也可以将域名转换成IP 1. 安装DNS服务 开始—〉设置—〉控制面板—〉添加/删除程序—〉添加/删除Windows组件—〉“网络服务”—〉选择“域名服 ...
- openssl生成ssl证书(转)
原文:http://blog.sina.com.cn/s/blog_4fd50c390101891c.html x509证书一般会用到三类文,key,csr,crt. Key 是私用密钥openssl ...
- Tomcat的windows10集群搭建(一台电脑同时运行多个tomcat配置方法)
配置方法(好久不配置了,忘记了,今天还是总结下吧): 1.官网下载tomcat ,我下载了tomcat6.0和tomcat7.0(以便区分) 官网地址:http://tomcat.apache.org ...
- myeclispe2014启动后报错 Subclipse talks to Subversion via a Java API that requires access to native libraries.
解决方案: Window -> Preferences -> Team -> SVN, 将SVN接口的Client修改为如图所示