1. 程式人生 > >BZOJ4017:小Q的無敵異或

BZOJ4017:小Q的無敵異或

長度 原因 stat con sum nlog 之前 時間復雜度 solution

Link

題意:有一個長度為 \(N\) 的數列,求其 \[\sum 所有子數列異或和\]
以及\[XORSUM \{子數列求和\}\]

\(1 \leq N \leq 10^5,元素 0 \leq A_i \leq 10^6\)

Solution

對於第一個子問題,按位計算貢獻,即計算ans能加多少個\(1 << k\)

首先我們計算一下前綴異或和

<font color=red,size=6>註:以下就以某一位來討論

我們維護兩個值,\(0\) 的個數以及 \(1\) 的個數
令當前已經計算了 \(k\) 個值的貢獻,要計算 \(k+1\) 的貢獻
如果 \(k+1\) 處前綴異或和這一位是 \(1\)

則貢獻要加上先前 \(0\) 的個數,否則要加上 \(1\) 的個數 (顯然,想要使對最終答案的貢獻為 \(1\) ,之前一定是 \(1 xor 0=1\) 或者 \(0 xor 1= 1\)
最後ans直接加上 \((1 << k)\times 貢獻值\) 即可

第一問就解決了,時間復雜度 \(O(nlog \ max\{A\})\)


第二個子問題:
答案是異或和,我們不妨按位來考慮,看什麽時候會使 \(ans\) 的某一位變成 \(1\)
顯然,由異或的性質可知,第 \(k\) 位 有奇數個 \(1\) 出現時,最終答案的第 \(k\) 位才為 \(1\)
形式化的,我們有
\[(sum(r)-sum(l-1)) mod 2^{k+1} \geq 2^k (r \geq l)\]


此時這題復雜度已經比純暴力好了非常多,對於每一個 \(r\) 統計出所有滿足上式的 \(l-1\) 的個數,但仍然很高
不過由上面的操作統計先前滿足條件的個數,想到可以使用樹狀數組或線段樹來維護, \(logn\) 查詢

上述式子可以進一步展開,
\[\begin{cases}{sum(l-1)mod2^{k+1}\leq sum(r) mod 2^{k+1}-2^k}\quad\quad(sum(r) mod 2^{k+1}\geq sum(l-1)mod2^{k+1})\\{sum(l-1)mod2^{k+1}\leq sum(r) mod 2^{k+1}+2^k}\quad\quad(sum(r) mod 2^{k+1} < sum(l-1)mod2^{k+1})\end{cases}\]

可以用樹狀數組分別查詢
代碼裏會看到查詢三個值,很詭異,原因見下圖
技術分享圖片
由之前推的兩個式子可知,兩條紅線之間夾的是不可取的地方,我們需要左右兩邊的值,如果用BIT存奇偶性,查找三個綠色處的前綴,異或起來就可以了(中間部分有兩遍異或,相當與不存在)

最後,每一位統計最終的奇偶性,若為奇數,則 \(ans|=(1 << k)\)


ps: \(\quad\) \(a\ mod \ 2^{k+1}\) 等價於 \(a \ \& \ (2^{k+1}-1)\) //二進制考慮一下


First Code(奇偶性):

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define MAXN 100010
#define MOD 998244353
using namespace std;

LL n;
LL sum[MAXN];
LL xsum[MAXN];
LL p[MAXN];
LL ans;

bool tree[MAXN];

int lowbit(int x){
    return (x)&(-x);
}

void change(LL pos){
    pos++;
    if(pos==0)
        return;
    LL i;
    for(i=pos;i<=n+1;i+=lowbit(i))
        tree[i]^=1;
}

LL getnum(LL pos){
    LL re=0;LL i;
    pos++;
    for(i=pos;i;i-=lowbit(i))
        re^=tree[i];
    return re;
}

LL getloc(LL x){
    LL l=0,r=n;
    LL ans=-1;
    while(l<=r){
        LL mid=(l+r)>>1;
        if(p[mid]<=x){
            l=mid+1;
            ans=mid;
        }
        else
            r=mid-1;
    }
    return ans;
}

void solve1(){
    LL k;
    LL i;
    LL cnt[2];
    for(k=0;k<=30;k++){
        cnt[0]=cnt[1]=0;
        LL tmp=0;
        for(i=0;i<=n;i++){
            bool tt=xsum[i]&(1<<k);
            tmp+=cnt[tt^1];
            cnt[tt]++;
        }
        ans += (1<<k) * tmp%MOD;
        ans%=MOD;
    }
    printf("%lld ",ans);
}

void solve2(){
    ans=0;
    LL i,k;
    for(k = 0;(1LL << k) <= sum[n];k++){
        LL tmp=0;
        for(i=0;i<=n;i++)
            p[i] = sum[i] & ((1LL << (k+1)) - 1);
        sort(p,p+n+1);
        memset(tree,0,sizeof(tree));
        for(i=0;i<=n;i++){
            LL now = sum[i] & ((1LL << (k + 1)) - 1);
            //printf("%lld %lld\n",now,getloc(now));
            LL t1=getnum(getloc(now-(1LL<<k)));
            LL t2=getnum(getloc(now+(1LL<<k)));
            LL t3=getnum(getloc(now));
            change(getloc(now));
            //printf("--%lld %lld %lld\n",now-(1LL<<k),now+(1LL<<k),now);
            //printf("::%lld %lld %lld\n",t1,t2,t3);
            tmp^=t1^t2^t3;
        }
        if(tmp)
            ans |= (1LL<<k);
    }
    printf("%lld\n",ans);
}

int main(){
    scanf("%lld",&n);
    LL i;
    LL x;
    for(i=1;i<=n;i++){
        scanf("%lld",&x);
        xsum[i]=xsum[i-1]^x;
        sum[i]=sum[i-1]+x;
    }
    solve1();
    solve2();
    return 0;
}

Second Code(統計個數):

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define MAXN 100010
#define MOD 998244353
using namespace std;

LL n;
LL sum[MAXN];
LL xsum[MAXN];
LL p[MAXN];
LL ans;

int tree[MAXN];

int lowbit(int x){
    return (x)&(-x);
}

void change(LL pos,int v){
    pos++;
    if(pos==0)
        return;
    LL i;
    for(i=pos;i<=n+1;i+=lowbit(i))
        tree[i]+=v;
}

LL getnum(LL pos){
    LL re=0;LL i;
    pos++;
    for(i=pos;i;i-=lowbit(i))
        re+=tree[i];
    return re;
}

LL getloc(LL x){
    LL l=0,r=n;
    LL ans=-1;
    while(l<=r){
        LL mid=(l+r)>>1;
        if(p[mid]<=x){
            l=mid+1;
            ans=mid;
        }
        else
            r=mid-1;
    }
    return ans;
}

void solve1(){
    LL k;
    LL i;
    LL cnt[2];
    for(k=0;k<=30;k++){
        cnt[0]=cnt[1]=0;
        LL tmp=0;
        for(i=0;i<=n;i++){
            bool tt=xsum[i]&(1<<k);
            tmp+=cnt[tt^1];
            cnt[tt]++;
        }
        ans += (1<<k) * tmp%MOD;
        ans%=MOD;
    }
    printf("%lld ",ans);
}

void solve2(){
    ans=0;
    LL i,k;
    for(k = 0;(1LL << k) <= sum[n];k++){
        LL tmp=0;
        for(i=0;i<=n;i++)
            p[i] = sum[i] & ((1LL << (k+1)) - 1);
        sort(p,p+n+1);
        memset(tree,0,sizeof(tree));
        for(i=0;i<=n;i++){
            LL now = sum[i] & ((1LL << (k + 1)) - 1);
            change(getloc(now),1);
            LL t1=getnum(getloc(now-(1LL<<k)));
            LL t2=getnum(getloc(now+(1LL<<k)));
            LL t3=getnum(getloc(now));
            tmp+=t1+t2-t3;
        }
        if(tmp%2)
            ans |= (1LL<<k);
    }
    printf("%lld\n",ans);
}

int main(){
    scanf("%lld",&n);
    LL i;
    LL x;
    for(i=1;i<=n;i++){
        scanf("%lld",&x);
        xsum[i]=xsum[i-1]^x;
        sum[i]=sum[i-1]+x;
    }
    solve1();
    solve2();
    return 0;
}

BZOJ4017:小Q的無敵異或