题目描述
萧薰儿是古国的公主,平时的一大爱好是采花。
今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。
花园足够大,容纳了n朵花,花有c种颜色(用整数1-c表示),且花是排成一排的,以便于公主采花。公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴!同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。
由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了m个行程,然后一一向你询问公主能采到多少朵花(她知道你是编程高手,定能快速给出答案!),最后会选择令公主最高兴的行程(为了拿到更多奖金!)。
说明
n,m,c≤2∗10^6
题解:
一句话题意:求询问区间内出现次数大于1的数有多少个。
直接处理不现实。可以离线。
询问按照左端点排序。
可以类比HH的项链、
预处理每个点nxt[i],表示i位置相同颜色的下一个位置。
树状数组每个点(点的值是c)为1的含义是,这个点在当前询问区间左端点为L的时候,是从L开始的第二个出现的这个数c
开始把所有颜色第二次出现的位置+1
那么对于所有的询问左端点为L的询问,直接query(r)-query(l-1)即可。
可以理解,第三次及以上出现的不算重,一次的也不会算。必须r包含第二次出现的才会统计上。
L右移的时候,把nxt[L]-=1,nxt[nxt[L]]+=1因为,nxt[L]原来是第二个,现在会变成第一个。
nxt[nxt[L]]就变成第二个了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2000000+5;
int n,m,c;
int has[N],nxt[N];
int a[N];
int f[N];
void add(int x,int d){
for(;x<=n;x+=x&(-x)) f[x]+=d;
}
int query(int x){
int ret=0;
for(;x;x-=x&(-x)) ret+=f[x];return ret;
}
struct node{
int l,r;
int id;
bool friend operator <(node a,node b){
return a.l<b.l;
}
}q[N];
int ans[N];
int pos[N];
int main()
{
scanf("%d%d%d",&n,&c,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
has[a[i]]++;
if(pos[a[i]])nxt[pos[a[i]]]=i;
if(has[a[i]]==2) add(i,1);
pos[a[i]]=i;
}
int l,r;
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}sort(q+1,q+m+1);
int L=1;
for(int i=1;i<=m;i++){
while(L<q[i].l){
if(nxt[L]) add(nxt[L],-1);
if(nxt[nxt[L]]) add(nxt[nxt[L]],1);
L++;
}
ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
}
for(int i=1;i<=m;i++){
printf("%d
",ans[i]);
}
return 0;
}
总结:
对于离线区间询问的情况——BY LYD:
1.cdq分治(对于前一半对后一半的影响)
2.整体二分(对于答案)
3.区间排序(对于处理答案先后顺序,便于区间之间转化的时候降低复杂度(经典的有如莫队))