BZOJ 2120 数颜色

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2120

题意:给出一个数列,两种操作:(1)询问区间[L,R]内有多少不同的数字;(2)修改某个位置的数字。

思路:首先分块。然后用sum[L][R][x]记录从第L块到第R块数字x出现的次数,cnt[L][R] 表示从第L块到第R块不同数字的个数。分块后可以预处理出初始时的sum和cnt数组。对于查询,[L,R],可能不是完整占据某段连续的块,需要将零头加上,之后输出答案再将零头减去;对于修改,找到包含修改位置所在块的所有区间修改sum和cnt数组。

 




int pl[25],pr[25],pNum,cnt[25][25],sum[25][25][N];
int f[N*100],K,size;
int n,m;
int a[N];


int F(int k)
{
    if(!f[k]) f[k]=++K;
    return f[k];
}




void deal()
{
    int x,y,i,j,k,ll,rr;
    char op[5];
    while(m--)
    {
        RD(op); RD(x,y);
        if(op[0]=='Q')
        {
            ll=0; rr=pNum;
            while(pl[ll]<x) ll++;
            while(pr[rr]>y) rr--;
            for(i=x;i<=min(y,pr[ll-1]);i++)
            {
                if(++sum[ll][rr][a[i]]==1) cnt[ll][rr]++;
            }
            for(i=max(x,pl[rr+1]);i<=y;i++)
            {
                if(++sum[ll][rr][a[i]]==1) cnt[ll][rr]++;
            }
            PR(cnt[ll][rr]);
            for(i=x;i<=min(y,pr[ll-1]);i++)
            {
                if(0==--sum[ll][rr][a[i]]) cnt[ll][rr]--;
            }
            for(i=max(x,pl[rr+1]);i<=y;i++)
            {
                if(0==--sum[ll][rr][a[i]]) cnt[ll][rr]--;
            }
        }
        else
        {
            k=0;
            while(pr[k]<x) k++;
            for(i=1;i<=k;i++) for(j=k;j<=pNum;j++)
            {
                if(0==--sum[i][j][a[x]]) cnt[i][j]--;
            }
            a[x]=F(y);
            for(i=1;i<=k;i++) for(j=k;j<=pNum;j++)
            {
                if(1==++sum[i][j][a[x]]) cnt[i][j]++;
            }
        }
    }
}


int main()
{
    RD(n,m);
    int i,j,k;
    FOR1(i,n) RD(j),a[i]=F(j);
    while(size*size*size<n) size++;
    size=size*size;
    int L=1,R=min(n,size);
    while(L<=n)
    {
        pl[++pNum]=L;
        pr[pNum]=R;
        L=R+1;
        R=min(n,R+size);
    }
    FOR1(i,pNum) for(j=i;j<=pNum;j++)
    {
        for(k=pl[i];k<=pr[j];k++)
        {
            if(++sum[i][j][a[k]]==1) cnt[i][j]++;
        }
    }
    pl[0]=-INF; pr[0]=0; pl[++pNum]=n+1; pr[pNum]=INF;
    deal();
}

原文地址:https://www.cnblogs.com/jianglangcaijin/p/3460040.html