BZOJ2120 数颜色 分块+二分法

Posted WDZRMPCBIT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ2120 数颜色 分块+二分法相关的知识,希望对你有一定的参考价值。

题意:给定一个颜色序列,维护:1、单点修改  2、区间查询不同颜色的种数

题解:

定义f[i]为i左边第一个和i颜色相同的位置,用分块来维护f。

询问:看区间中有多少个位置的f[i]<l

更新:暴力枚举p左右最近的与p颜色相同的位置,更新即可

#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXS=100+2;
const int MAXC=1000000+2;
struct BLOCK{
    int a[MAXS],b[MAXS],c[MAXS];
}block[MAXS];
int N,M,S,C,f[MAXC];
char s[3];

void Change(int x,int c){
    int p=(x-1)/S+1,m=(1+S)>>1;
    for(int l=1,r=S;l<r;m=(l+r)>>1){
        if(block[p].b[m]<block[p].a[x-(p-1)*S]) l=m+1;
        else r=m;
    }

    block[p].b[m]=c;
    while(m!=1 && block[p].b[m-1]>block[p].b[m]) swap(block[p].b[m-1],block[p].b[m]),m--;
    while(m!=S && block[p].b[m+1]<block[p].b[m]) swap(block[p].b[m+1],block[p].b[m]),m++;

    block[p].a[x-(p-1)*S]=c;
}

void Update(int x,int c){
    int p=(x-1)/S+1,t=-1;

    for(int i=x+1;i<=N;i++){
        if(block[(i-1)/S+1].c[i-(i-1)/S*S]==block[p].c[x-(p-1)*S]){
            Change(i,block[p].a[x-(p-1)*S]);
            break;
        }
    }

    for(int i=x+1;i<=N;i++)
        if(block[(i-1)/S+1].c[i-(i-1)/S*S]==c){
            Change(i,x);
            break;
        }

    Change(x,-1);
    for(int i=x-1;i;i--)
        if(block[(i-1)/S+1].c[i-(i-1)/S*S]==c){
            Change(x,i);
            break;
        }

    block[p].c[x-S*(p-1)]=c;
}

int Find(int p,int t){
    int m=(1+S)>>1,l=1,r=S;
    while(l<r){
        if(block[p].b[m]>=t) r=m-1;
        else l=m+1;
        m=(l+r)>>1;
    }
    if(block[p].b[l]>=t) l--;
    return l;
}

int Query(int l,int r){
    int ret=0,m=(1+S)>>1,t=l;
    if((l-1)%S){
        int p=(l-1)/S+1;
        for(int i=l-S*(p-1);i<=S && i<=r-S*(p-1);i++)
            if(block[p].a[i]<t) ret++;
        l=S*p+1;
    }

    while(l+S-1<=r) ret+=Find((l-1)/S+1,t),l+=S;

    if(r%S){
        int p=l/S+1;
        for(int i=1;i<=r-S*(p-1);i++)
            if(block[p].a[i]<t) ret++;
    }

    return ret;
}

int main(){
    memset(f,-1,sizeof(f));
    memset(block,0X7F,sizeof(block));

    scanf("%d %d",&N,&M),S=ceil(sqrt((double)N));
    for(int i=1,j=1,k=1,c;i<=N;i++,j++){
        scanf("%d",&block[k].c[j]);
        block[k].a[j]=block[k].b[j]=f[block[k].c[j]],f[block[k].c[j]]=i;
        if(j==S || i==N){
            sort(block[k].b+1,block[k].b+j+1);
            j=0,k++;
        }
    }
    C=(N%S?N/S+1:N/S);

    for(int i=1,a,b;i<=M;i++){
        scanf("%s %d %d",s,&a,&b);
        if(s[0]==\'R\') Update(a,b);
        if(s[0]==\'Q\') printf("%d\\n",Query(a,b));
    }

    return 0;
}
View Code

 

以上是关于BZOJ2120 数颜色 分块+二分法的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ2453: 维护队列&&BZOJ2120: 数颜色 二分+分块 双倍经验

bzoj2120 数颜色

BZOJ 2120 数颜色 (分块,蛮力)

bzoj2120: 数颜色 [莫队][分块]

Bzoj 2453: 维护队列 && Bzoj 2120: 数颜色 分块,bitset

bzoj2120: 数颜色(BIT套主席树+set/分块)