原题链接:https://www.luogu.com.cn/problem/P4113
采花
题目描述
萧薰儿是古国的公主,平时的一大爱好是采花。
今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。
花园足够大,容纳了 n n n 朵花,共有 c c c 种颜色,用整数 1 ∼ c 1 \sim c 1∼c 表示。且花是排成一排的,以便于公主采花。公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴。同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。
由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了 m m m 个行程,然后一一向你询问公主能采到的花共有几种不同的颜色。
输入格式
输入的第一行是三个用空格隔开的整数,分别代表花的个数 n n n,花的颜色数 c c c,以及行程数 m m m。
输入的第二行是 n n n 个用空格隔开的整数,第 i i i 个整数代表第 i i i 朵花的颜色 x i x_i xi 。
第 3 3 3 行到第 ( m + 2 ) (m + 2) (m+2) 行,每行两个整数 l , r l, r l,r,第 ( i + 2 ) (i + 2) (i+2) 行的数字代表第 i i i 次行程为第 l l l 到第 r r r 朵花。
输出格式
共输出 m m m 行,每行一个整数。第 i i i 行的整数代表第 i i i 次行程公主能采到的花共有几种不同的颜色。
输入输出样例
输入 #1
5 3 5
1 2 2 3 1
1 5
1 2
2 2
2 3
3 5
输出 #1
2
0
0
1
0
说明/提示
输入输出样例 1 1 1 解释
共有五朵花,颜色分别为 1 , 2 , 2 , 3 , 1 1,~2,~2,~3,~1 1, 2, 2, 3, 1。
对于第一次行程,公主采花的区间为 [ 1 , 5 ] [1, 5] [1,5],可以采位置 1 , 2 , 3 , 5 1,~2,~3,~5 1, 2, 3, 5 处的花,共有 1 1 1 和 2 2 2 两种不同的颜色。
对于第二次行程,公主采花的区间为 [ 1 , 2 ] [1, 2] [1,2],但是颜色为 1 1 1 和 2 2 2 的花都只出现了一次,因此公主无花可采。
对于第三次行程,公主采花的区间为 [ 2 , 2 ] [2, 2] [2,2],但是颜色为 2 2 2 的花只出现了一次,公主无花可采。
对于第四次行程,公主采花的区间为 [ 2 , 3 ] [2, 3] [2,3],可以采 2 , 3 2,~3 2, 3 位置的花,只有 2 2 2 这一种颜色。
对于第五次行程,公主采花的区间为 [ 3 , 5 ] [3,5] [3,5],但是颜色为 1 , 2 , 3 1, 2, 3 1,2,3 的花都只出现了一次,因此公主无花可采。
数据范围与约定
本题采用多测试点捆绑测试,共有两个子任务。
对于子任务 1 1 1,分值为 100 100 100 分,保证 1 ≤ n , c , m ≤ 3 × 1 0 5 1 \leq n, c, m \leq 3 \times 10^5 1≤n,c,m≤3×105 。
对于子任务 2 2 2,分值为 100 100 100 分,保证 1 ≤ n , c , m ≤ 2 × 1 0 6 1 \leq n, c, m \leq 2 \times 10^6 1≤n,c,m≤2×106。
对于全部的测试点,保证 1 ≤ x i ≤ c 1 \leq x_i \leq c 1≤xi≤c, 1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1≤l≤r≤n。
题解
HH的项链升级版,鉴于之前写的题解比较水,今天就赎一下罪。
首先依然是把所有询问离线,按照右端点排序,之后按顺序处理每一个询问。因为题目要求每个颜色出现两次,我们用 l s t 1 [ i ] lst1[i] lst1[i]记录颜色 i i i上一次出现的位置、 l s t 2 [ i ] lst2[i] lst2[i]记录颜色 i i i上上次出现的位置。如果这两个数组记录的都不为空,那么这个颜色一定出现了两次,我们就在树状数组里给该颜色上上次出现的位置加上 1 1 1,比如对于 1 , 2 , 2 , 3 , 1 , 2 1,~2,~2,~3,~1,~2 1, 2, 2, 3, 1, 2,树状数组维护的序列变化情况就应该如下所示:
{ 0 } → { 0 , 0 } → { 0 , 1 , 0 } → { 0 , 1 , 0 , 0 } → { 1 , 1 , 0 , 0 , 0 } → { 1 , 0 , 1 , 0 , 0 , 0 } \{0\}\to\{0,0\}\to\{0,1,0\}\to\{0,1,0,0\}\to\{1,1,0,0,0\}\to\{1,0,1,0,0,0\} {
0}→{
0,0}→{
0,1,0}→{
0,1,0,0}→{
1,1,0,0,0}→{
1,0,1,0,0,0}
随时随地只有上上次出现该颜色的位置为 1 1 1就保证了当我们按照右端点升序的方式处理询问区间的时候,如果区间里有 1 1 1,那么该颜色一定在区间里出现了复数次。
代码
这钵啊,这钵是其他部分一次对,一个sort
错两次……
码风放飞自我系列:
#include<bits/stdc++.h>
#define lb(v) (v&(-v))
using namespace std;
const int M=2e6+5;
int n,c,m,col[M],lst1[M],lst2[M],tree[M],ans[M];
struct Ask{
int id,le,ri;}ask[M];
bool cmp(Ask a,Ask b){
return a.ri==b.ri?a.le<b.le:a.ri<b.ri;}
void add(int v,int delta){
for(;v&&v<=n;v+=lb(v))tree[v]+=delta;}
int sum(int v){
int r=0;for(;v;v-=lb(v))r+=tree[v];return r;}
void in()
{
scanf("%d%d%d",&n,&c,&m);
for(int i=1;i<=n;++i)scanf("%d",&col[i]);
for(int i=1;i<=m;++i)scanf("%d%d",&ask[i].le,&ask[i].ri),ask[i].id=i;
}
void ac()
{
sort(ask+1,ask+m+1,cmp);
for(int i=1,p=1;i<=m;ans[ask[i].id]=sum(ask[i].ri)-sum(ask[i].le-1),++i)
for(;p<=ask[i].ri;lst1[col[p]]=p,++p)
if(lst1[col[p]])add(lst2[col[p]],-1),add(lst2[col[p]]=lst1[col[p]],1);
for(int i=1;i<=m;++i)printf("%d\n",ans[i]);
}
int main()
{
in(),ac();
system("pause");
}