【洛谷P4887】【模板】莫隊二次離線(第十四分塊(前體))
阿新 • • 發佈:2021-10-04
題目
題目連結:https://www.luogu.com.cn/problem/P4887
珂朵莉給了你一個序列 \(a\),每次查詢給一個區間 \([l,r]\),查詢 \(l \leq i< j \leq r\),且 \(a_i \oplus a_j\) 的二進位制表示下有 \(k\) 個 \(1\) 的二元組 \((i,j)\) 的個數。\(\oplus\) 是指按位異或。
\(n,m\leq 10^5\),\(0\leq a_i<2^{14}\)。
思路
按照莫隊二次離線的套路後需要支援的是 \(O(n\sqrt{n})\) 個形如 \([1,i]\) 中有多少個數與 \(j\) 異或的二進位制表示下恰好有 \(k\)
注意到 \(a\leq 2^{14}\),也就是說,二進位制表示下恰好有 \(k\) 個 \(1\) 的數量,最大值為 \(k=7\) 時 \(\binom{14}{7}=3432\) 個。
預處理出所有二進位制表示下有 \(k\) 個 \(1\) 的數,依次處理所有二次離線後的詢問,開一個桶記錄當 \(a_j=x\) 時,\(1\sim i\) 有多少個數與 \(x\) 異或起來二進位制下有 \(k\) 個 \(1\)。當 \(i\) 增加時,桶最多更新 \(3432\) 個位置。
時間複雜度 \(O(n\sqrt n+3432 n)\)。空間複雜度 \(O(n+m)\)
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010,B=320; int n,m,k,Q,a[N],b[N],bel[N]; ll ans[N],sum1[N],sum2[N],cnt1[N],cnt2[N]; struct node { int l,r,id; }ask[N]; vector<node> L[N],R[N]; bool cmp(node x,node y) { if (bel[x.l]!=bel[y.l]) return x.l<y.l; return x.r<y.r; } int main() { scanf("%d%d%d",&n,&Q,&k); for (int i=0;i<16384;i++) { int cnt=0; for (int j=0;j<=14;j++) if (i&(1<<j)) cnt++; if (cnt==k) b[++m]=i; } for (int i=1;i<=n;i++) { scanf("%d",&a[i]); sum1[i]=sum1[i-1]+cnt1[a[i]]; for (int j=1;j<=m;j++) cnt1[a[i]^b[j]]++; bel[i]=(i-1)/B+1; } for (int i=n;i>=1;i--) { sum2[i]=sum2[i+1]+cnt2[a[i]]; for (int j=1;j<=m;j++) cnt2[a[i]^b[j]]++; } memset(cnt1,0,sizeof(cnt1)); memset(cnt2,0,sizeof(cnt2)); for (int i=1;i<=Q;i++) { scanf("%d%d",&ask[i].l,&ask[i].r); ask[i].id=i; } sort(ask+1,ask+1+Q,cmp); for (int i=1,l=1,r=0;i<=Q;i++) { int id=ask[i].id; if (l>ask[i].l) { ans[id]+=sum2[ask[i].l]-sum2[l]; R[r+1].push_back((node){ask[i].l,l-1,-id}); l=ask[i].l; } if (r<ask[i].r) { ans[id]+=sum1[ask[i].r]-sum1[r]; L[l-1].push_back((node){r+1,ask[i].r,-id}); r=ask[i].r; } if (l<ask[i].l) { ans[id]-=sum2[l]-sum2[ask[i].l]; R[r+1].push_back((node){l,ask[i].l-1,id}); l=ask[i].l; } if (r>ask[i].r) { ans[id]-=sum1[r]-sum1[ask[i].r]; L[l-1].push_back((node){ask[i].r+1,r,id}); r=ask[i].r; } } for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) cnt1[a[i]^b[j]]++; for (int j=0;j<(int)L[i].size();j++) { int id=abs(L[i][j].id),f=(L[i][j].id>0)?1:-1; for (int l=L[i][j].l;l<=L[i][j].r;l++) ans[id]+=f*cnt1[a[l]]; } } for (int i=n;i>=1;i--) { for (int j=1;j<=m;j++) cnt2[a[i]^b[j]]++; for (int j=0;j<(int)R[i].size();j++) { int id=abs(R[i][j].id),f=(R[i][j].id>0)?1:-1; for (int l=R[i][j].l;l<=R[i][j].r;l++) ans[id]+=f*cnt2[a[l]]; } } for (int i=1;i<=Q;i++) ans[ask[i].id]+=ans[ask[i-1].id]; for (int i=1;i<=Q;i++) cout<<ans[i]<<"\n"; return 0; }