Codeforces #340 div2 E. XOR and Favorite Number(莫隊算法)

題目鏈接:

http://codeforces.com/contest/617/problem/E

題目大意:

有n 個數,m個詢問。每次詢問在區間[l,r]裏面,有多少種情況使得ai^ai+1^……^aj=k。

範圍:1 ≤ n, m ≤ 100 0000 ≤ k ≤ 1 000 000。

思路:

對於這類區間上的問題,我們可以獲得數組的異或前綴和pre[i]=a1^a2......ai。

這樣對於區間[l,r],我們可以知道這個區間的異或值爲pre[l-1]^pre[r]。

對於不需要修改的區間詢問問題,可以採用莫隊算法。

莫隊算法:在我現在粗淺的認知看來,莫隊算法的主要精髓就是面對已知的詢問信息,將這些詢問進行順序調整然後離線操作。

例如對於多個詢問[l1,r1],[l2,r2]……[li,ri],如果已知了[l1,r1]的結果,那麼我們肯定很快能夠求出[l1+1,r1+1],[l1-1,r1+1],[l1-1,r1-1],[l1+1,r1-1]的結果。所以當我們去看第二個詢問的時候,我們就可以通過第一個詢問的結論,一步步地從[l1,r1]走向[l2,r2],此時我們所需要的時間就是O(|L2-L1|+|R2-R1|),這個有一個名字叫做曼哈頓距離。於是問題就變成了構造曼哈頓最小生成樹,然後按照生成樹的順序進行求解,使得速度大大提升。

但是求曼哈頓生成樹比較困難,然而還有一種分塊思想,能夠比較迅速而高效地解決這個問題。

我們把n個數分塊成爲sqrt(n)塊,然後把詢問進行排序:如果兩個詢問的l是在同一塊裏面的,就按r從小到大排。如果不同塊,就按照塊從小到大排。在同一個塊裏面轉移的時候,由於每個塊裏面有n^0.5個元素,所以轉移最大時間複雜度就是O((n^0.5)*(n^0.5))=O(n)。如果在不同塊,最大複雜度是O(n^1.5)。所以最終時間複雜度爲O(n^1.5)。

------------------------------------------------------------------------------

所以對於這個問題,我們已經知道了前綴和,所以可以知道:如果pre[i-1]^pre[j]=k,那麼就有pre[j]^k=pre[i-1]。這個時候我們開一個數組num,來記錄pre[i-1]^pre[j]=k時候的個數,我們轉移時就只需要對答案sum+=num[pre[i-1]^pre[j]]就可以了,再化簡一下就是,sum+=num[pre[j]^k]。

代碼:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdlib.h>
#define ll __int64
#define M 100010
using namespace std;
ll n,m,k,a[M],pre[M],ans[M],block,pos[M],num[M*50],sum;
struct node {
 ll l,r,id;
}Q[M];
bool cmp(node a,node b )
{
    if(pos[a.l]==pos[b.l])return a.r<b.r;
    return pos[a.l]<pos[b.l];
}
int main()
{
    ll i,j;
    while(~(scanf("%I64d%I64d%I64d",&n,&m,&k)))
    {
        sum=0;
        memset(num,0,sizeof(num));
        memset(pre,0,sizeof(pre));
        block=ceil(sqrt(1.0*n)); //分塊
        for(i=1;i<=n;i++)
        {
            scanf("%I64d",&a[i]);
            if(i==1)pre[i]=a[i];
            else pre[i]=pre[i-1]^a[i];    //求前綴數組
            pos[i]=(i-1)/block;
        }
        for(i=1;i<=m;i++)
        {
            scanf("%I64d%I64d",&Q[i].l,&Q[i].r);
            Q[i].id=i;
         //   pos[i]=i/block;
        }
        sort(Q+1,Q+m+1,cmp);
        ll L,R;
        L=1;R=0;
        num[0]++;
        for(i=1;i<=m;i++)
        {
            ll id=Q[i].id;
            while(R<Q[i].r){
                R++;
                sum+=num[pre[R]^k];
                num[pre[R]]++;   //記錄出現次數
            }

            while(R>Q[i].r)
            {
                num[pre[R]]--;
                sum-=num[pre[R]^k];
                R--;
            }

           while(L<Q[i].l)
            {
                num[pre[L-1]]--;
                sum-=num[pre[L-1]^k];
                L++;
            }

            while(L>Q[i].l)
            {
                L--;
                sum+=num[pre[L-1]^k];
                num[pre[L-1]]++;

            }

           // printf("%I64d %I64d\n",L,R);
            ans[id]=sum;
        }
        for(i=1;i<=m;i++)
            printf("%I64d\n",ans[i]);
    }
}


發佈了235 篇原創文章 · 獲贊 4 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章