BZOJ 2453 维护队列 | 分块

题目:

http://www.lydsy.com/JudgeOnline/problem.php?id=2453


题解:

考虑维护每个位置的颜色上一次出现在哪里,计为pre[i],在询问l到r的时候,如果pre[i]<l,ans++

所以每次询问时整块的按pre排序,之后的做法类似教主的魔法

 
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 10005
#define M 1000005
using namespace std;
int n,q,m,block;
int c[N],pos[N],pre[N],b[N],last[M],x,y;
char ch[5];
int find(int x,int v)
{
    int l=(x-1)*block+1,r=min(x*block,n);
    int first=l;
    while (l<=r)
    {
        int mid=l+r>>1;
        if (pre[mid]<v) l=mid+1;
        else r=mid-1;
    }
    return l-first;
} 
void reset(int x)
{
    int l=(x-1)*block+1,r=min(x*block,n);
    for (int i=l;i<=r;i++) pre[i]=b[i];
    sort(pre+l,pre+r+1);
}
void build()
{
    for (int i=1;i<=n;i++)
    {
        b[i]=last[c[i]];
        last[c[i]]=i;
        pos[i]=(i-1)/block+1;
    }
    for (int i=1;i<=m;i++) reset(i);
}
int ask(int l,int r)
{
    int ans=0;
    if (pos[l]==pos[r])
    {
        for (int i=l;i<=r;i++)
            if (b[i]<l) ans++;
    }
    else
    {
        for (int i=l;i<=block*pos[l];i++) if (b[i]<l) ans++;
        for (int i=block*(pos[r]-1)+1;i<=r;i++) if (b[i]<l) ans++; 
    }
    for (int i=pos[l]+1;i<pos[r];i++)
        ans+=find(i,l);
    return ans;
}
void change(int x,int v)
{
    for (int i=1;i<=n;i++) last[c[i]]=0;
    c[x]=v;
    for (int i=1;i<=n;i++)
    {
        int t=b[i];
        b[i]=last[c[i]];
        if (t!=b[i]) reset(pos[i]);
        last[c[i]]=i;
    }
}
int main()
{
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++)
        scanf("%d",&c[i]);
    block=int(sqrt(n)+log(2*n)/log(2));
    if (n%block) m=n/block+1;
    else m=n/block; 
    build();
    for (int i=1;i<=q;i++)
    {
        scanf("%s%d%d",ch,&x,&y);
        if (ch[0]=='Q') printf("%d
",ask(x,y));
        else change(x,y);
    } 
    return 0;
} 
原文地址:https://www.cnblogs.com/mrsheep/p/8182013.html