「知識學習&日常訓練」莫隊演算法(一)(Codeforce Round #340 Div.2 E)
阿新 • • 發佈:2018-11-09
題意
已知一個長度為\(n\)的整數數列\(a[1],a[2],…,a[n]\),給定查詢引數\(l,r\),問\([l,r]\)內,有多少連續子段滿足異或和等於\(k\)。
也就是說,對於所有的\(x,y (l\le x\le y\le r)\),能夠滿足\(a[x]\oplus a[x+1]\oplus ...\oplus a[y]=k\)的\((x,y)\)有多少組。
分析
對於這種離線區間的查詢問題(不涉及對區間的更改),我們可以使用莫隊演算法解決。這類問題是什麼型別?對於序列上的區間詢問問題,如果從\([l, r]\)的答案能夠\(O(1)\)擴充套件到\([l+1,r],[l,r−1],[l - 1, r],[l, r + 1]\) 的答案,那麼可以在\(O(n\sqrt n)\)的複雜度內求出所有詢問的答案。
這題為什麼可以?因為對於\(x\)至\(y\)的區間異或和,我們可以用字首異或和的\(x-1\)與\(y\)相異或來解決。
接下來講講具體的實現:
(參考:https://blog.sengxian.com/algorithms/mo-s-algorithm)
實現:離線後排序,順序處理每個詢問,暴力從上一個區間的答案轉移到下一個區間答案。
排序方法:設定塊的長度為\(S\),按照\((\lfloor\frac l S\rfloor, r)\)二元組從小到大排序。
複雜度分析:設序列長度為\(n\),詢問個數為\(m\)。可以發現從\((l_1, r_1)\)
這題的具體實現:我們記\(mp[x]\)為異或和為x的個數。轉移區間的時候(不失一般性,考慮區間純右移),每增加一個點\(r\)
程式碼
參考:https://blog.csdn.net/swust_lian/article/details/50615109
/*
* Filename: cfr340d2e.cpp
* Date: 2018-11-09
*/
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
#define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
#define per(i,a,b) for(repType i=(a); i>=(b); --i)
#define ZERO(x) memset(x, 0, sizeof(x))
#define MS(x,y) memset(x, y, sizeof(x))
#define ALL(x) (x).begin(), (x).end()
#define QUICKIO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
using namespace std;
using pi=pair<int,int>;
using repType=int;
using ll=long long;
using ld=long double;
using ull=unsigned long long;
const int MAXN=100005;
const int BLOCK=400;
struct Node
{
ll l,r,id;
Node(ll _l=0, ll _r=0, ll _id=0):
l(_l), r(_r), id(_id) {}
bool operator < (const Node& rhs) const
{
if(l/BLOCK!=rhs.l/BLOCK) return l/BLOCK<rhs.l/BLOCK;
else return r<rhs.r;
}
};
vector<Node> vec;
ll s[MAXN];
ll ans[MAXN], mp[MAXN*200];
ll n,m,k;
int
main()
{
scanf("%lld%lld%lld", &n, &m, &k);
s[0]=0;
rep(i,1,n)
{
ll x; scanf("%lld", &x);
s[i]=s[i-1]^x;
}
rep(i,1,m)
{
ll l,r;
scanf("%lld%lld", &l, &r);
vec.PB(l-1,r,i); // why l-1: xor(a[x]~a[y])=k <-> s[x-1]^s[y]=k
}
sort(ALL(vec));
ZERO(mp);
ZERO(ans);
ll tmp=0;
int l=vec[0].l, r=vec[0].r;
rep(i,l,r)
{
tmp+=mp[s[i]^k];
mp[s[i]]++;
}
ans[vec[0].id]=tmp;
rep(i,1,m-1)
{
int L=vec[i].l,
R=vec[i].r;
while(l>L)
{
l--;
tmp+=mp[s[l]^k];
mp[s[l]]++; // mp: cnt of xor_sum = s[l]
}
while(l<L)
{
mp[s[l]]--;
tmp-=mp[s[l]^k];
l++;
}
while(r<R)
{
r++;
tmp+=mp[s[r]^k];
mp[s[r]]++;
}
while(r>R)
{
mp[s[r]]--;
tmp-=mp[s[r]^k];
r--;
}
ans[vec[i].id]=tmp;
}
rep(i,1,m) printf("%lld\n", ans[i]);
return 0;
}