[HEOI2012]采花

题目描述

萧薰儿是古国的公主,平时的一大爱好是采花。

今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。

花园足够大,容纳了n朵花,花有c种颜色(用整数1-c表示),且花是排成一排的,以便于公主采花。公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴!同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。

由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了m个行程,然后一一向你询问公主能采到多少朵花(她知道你是编程高手,定能快速给出答案!),最后会选择令公主最高兴的行程(为了拿到更多奖金!)。

说明

n,m,c210^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.区间排序(对于处理答案先后顺序,便于区间之间转化的时候降低复杂度(经典的有如莫队))

原文地址:https://www.cnblogs.com/Miracevin/p/9676591.html