【挖坑記】JZOJ 4711 Binary

題目大意

       給定一個長度爲n的整數數列a和q次操作:
       修改操作:形如 1 x y,表示將ax 的值修改爲y;
       詢問操作:形如 2 x y,表示詢問n1(ai+x) and y 的值。
      n,q<=105
      0<=ai,x,y<=220

【40%】n,q<=5000

       題目怎麼說怎麼做。

【另20%】所有詢問的x=0

       二進制總共只有20位,直接記錄每一位爲1的有多少個數,假設記爲cnt[i]查詢時,y的第i個二進制位爲1,就答案加上2icnt[i]

【100%】n,q<=10^5

       我們還是對於每個二進制位單獨考慮,且y這一位爲1才考慮。
       不考慮+x時,我們還可以弄一棵值域線段樹,那麼cnt[i]所包含的數就是這樣的:比i高的位任意,i位以內滿足在[2i1,2i1] 這個區間內。可以發現我們查詢的區間對於整個值域來說並不連續,而是一段一段的,因此我們對每個二進制位都開一棵值域線段樹,第i位的線段樹存儲的數則由ai 變爲ai mod 2i 。這樣我們操作第i位時,直接在第i位的線段樹中查詢[2i1,2i1] 內的數有多少個,就行了。
       接下來考慮+x。這個實際上是對查詢區間的位移,比如當前查詢區間[2i1,2i1] ,那麼就變成[2i1x,2i1x] 。要注意這裏的x是mod 2i 的。唯一的問題就是區間左端點可能爲負。這好辦,先把0到右端點的正常操作,假設左端點變成了z ,那我們再查詢[2iz,2i1] 就行了。這裏相當於是退位一樣的東西。

代碼

//我把線段樹換成樹狀數組,這樣常數小代碼短

#include<cstdio>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;

const int maxn=(1e5)+5, MX=20, maxc=(1<<20)+5;

int n,a[maxn],er[MX+5];

int c[MX+2][maxc];
int lowbit(int x) {return x&(-x);}
void xg(int ty,int x,int z)
{
    if (x==0) {c[ty][0]+=z; return;}
    for(; x<er[ty]; x+=lowbit(x)) c[ty][x]+=z;
}
int get(int ty,int x)
{
    if (x<0) return 0;
    int re=0;
    for(; x; x-=lowbit(x)) re+=c[ty][x];
    return re+c[ty][0];
}

int q;
int main()
{
    fo(i,0,MX) er[i]=1<<i;

    scanf("%d %d",&n,&q);
    fo(i,1,n)
    {
        scanf("%d",&a[i]);
        fo(j,1,20) xg(j,a[i]%er[j],1);
    }

    while (q--)
    {
        int ty,x,y;
        scanf("%d %d %d",&ty,&x,&y);
        if (ty==1)
        {
            fo(i,1,20) xg(i,a[x]%er[i],-1);
            fo(i,1,20) xg(i,y%er[i],1);
            a[x]=y;
        } else
        {
            LL ans=0;
            fo(i,1,MX) if (y&er[i-1])
            {
                int xx=x%er[i], st=er[i-1]-xx, en=er[i]-1-xx;
                if (st<0)
                {
                    ans+=(LL)er[i-1]*get(i,en);
                    if (i>1) ans+=(LL)er[i-1]*(get(i,er[i]-1)-get(i,er[i]+st-1));
                } else
                {
                    ans+=(LL)er[i-1]*(get(i,en)-get(i,st-1));
                }
            }

            printf("%lld\n",ans);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章