[BZOJ 2743] 采花

Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2743

 

Algorithm:

此题询问区间内出现次数超过1个的数字

明显在线做无从下手,无法在区间两端无序的情况下统计符合要求的数字

 

但可以发现,如果左端递增,是可以用树状数组维护右端数据的

于是我们采取离线方式,将询问排序,左端点不断右移,树状数组随之更新即可

 

先预处理出next和first数组

每次走过一个数对nxt[i]--,nxt[nxt[i]]++

 

Code:

#include <bits/stdc++.h>

using namespace std;
typedef pair<int,int> P;
typedef pair<P,int> PP;
#define F first
#define S second

const int MAXN=1000010;
int dat[MAXN],bit[MAXN],res[MAXN],nxt[MAXN],cur[MAXN],fst[MAXN],n,c,m;
PP op[MAXN];

inline int read()
{
    char ch;int num,f=0;
    while(!isdigit(ch=getchar())) f|=(ch=='-');
    num=ch-'0';
    while(isdigit(ch=getchar())) num=num*10+ch-'0';
    return f?-num:num;
}

inline void write(long long x)
{
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

void update(int pos,int x)
{
    while(pos<=n)
    {
        bit[pos]+=x;
        pos+=pos&(-pos);
    }
}

int cal(int pos)
{
    int ret=0;
    while(pos)
    {
        ret+=bit[pos];
        pos-=pos&(-pos);
    }
    return ret;
}

int main()
{
    n=read();c=read();m=read();
    fill(nxt,nxt+MAXN,MAXN-2);
    for(int i=1;i<=n;i++) dat[i]=read();
    for(int i=1;i<=m;i++) op[i].F.F=read(),op[i].F.S=read(),op[i].S=i;
    for(int i=1;i<=n;i++)  //预处理
    {
        if(!cur[dat[i]]) fst[dat[i]]=i;
        nxt[cur[dat[i]]]=i,cur[dat[i]]=i;
    }
    for(int i=1;i<=c;i++) if(nxt[fst[i]]) update(nxt[fst[i]],1);
    
    nxt[0]=0;op[0].F.F=1;
    sort(op+1,op+m+1);
    
    for(int i=1;i<=m;i++)  //离线
    {
        for(int j=op[i-1].F.F;j<op[i].F.F;j++)  //更新右端点
            update(nxt[nxt[j]],1),update(nxt[j],-1);
        res[op[i].S]=cal(op[i].F.S)-cal(op[i].F.F-1);
    }
    
    for(int i=1;i<=m;i++) write(res[i]),putchar('\n');
    return 0;
}

 

Review:

1、当数据维护与左端点的单调性有关时

      考虑排序后离线解题

 

2、一般涉及询问出现字符个数的问题

     要预处理出nxt数组,滑动窗口来解题

posted @ 2018-05-19 12:06  NewErA  阅读(145)  评论(0编辑  收藏  举报