原題鏈結
給定乙個長度為 \(n\) 的序列和乙個常數 \(k\),每次詢問乙個區間 \([l,r]\) 內,有多少對 \(i,j\),滿足 \(l\leq i,且 \(a_i \oplus a_j\) 的二進位制表示下恰好有 \(k\) 位為 \(1\)。
資料範圍
\(1 \leq n,m \leq 10^5,0 \leq a_i <2^,0\leq k\leq 14\)。
按照普通莫隊的思路,設當前的指標為 \(l,r\),詢問的區間為 \(l,r\),\(s_\) 表示 \(1 \sim x\) 中有多少數與 \(a_y\) 配對。以 \(r\) 轉移到 \(r+1\) 為例,更新的答案就為 \(s_-s_\) 。對於形如 \(s_\) ,可以直接預處理(後面會提到),但是對於 \(s_\) 這類問題,直接預處理的代價太大,於是就可以考慮二次離線處理。
首先考慮預處理 \(s_\)。記 \(f[i]\) 表示 \(1\sim i\) 中有多少數與 \(a[i+1]\) 配對,\(w[i][x]\) 表示 \(1\sim i\) 中有多少數與 \(x\) 配對。顯然,有 \(f[i]=w[i][a[i+1]]\)。
對於等式 \(a \oplus b=y\),兩邊同時異或 \(b\),由於 \(b \oplus b=0,a \oplus 0=a\),得到 \(a=b \oplus y\),而 \(y\) 就是所有滿足二進位制下有 \(k\) 位為 \(1\) 的數。由於 \(a\oplus b <2^\),所以 \(y\) 一共有 \(c_^\) 個,也就是最多只有 \(3432\) 個。
考慮從 \(1 \sim n\) 列舉 \(a_i\),再列舉所有的 \(y\),令 \(g[i][a_i \oplus y]++\),由於 \(g[i+1][x]\) 和 \(g[i][x]\) 只存在 \(a[i+1]\) 的差別,可以直接略去第一維。此時,\(g[x]\) 就表示 \(1\sim i\) 中有多少數與 \(x\) 配對,那麼就可以直接更新 \(f[i]=g[a[i+1]]\)。至此預處理完畢,時間複雜度為 \(o(c_^n)\)。
莫隊轉移時,分為四類討論需要二次離線的詢問。
\(1.\)從 \(r\) 轉移到 \(r\) 。總增加的貢獻為 \(\sum_^ s_-s_\)。前面一部分直接用 \(\sum_^ f[i]\) 轉移即可。後面一部分則記錄到 \(l-1\) 的詢問中二次離線處理。
\(2.\)從 \(r\) 轉移到 \(r\)。思路同上,但是需要注意此時是減去貢獻,所以需要改變一下符號,具體實現可以看**。
3.從 \(l\) 轉移到 \(l\)。總增加的貢獻為 \(\sum_^ s_-s_\)。此時需要注意,儘管 \(i=i\) 的情況在題意中是不合法的,但是此時仍然需要特判 \(k=0\) 的情況,因為儘管 \(f[i]\) 中不包含異或自己的情況,但是後面會提到,在二次離線是是會把這類情況算進去的。
4.從 \(l\) 轉移到 \(l\)。思路同上,也別忘了變符號。
下面討論二次離線的寫法。
對於每乙個 \(i\),設前面莫隊的時候需要加上乙個 \(p*\sum_^s_\) (其中 \(|p|=1\),表示整體的正負符號)。此時 \(j\) 和 \(i\) 的大小關係就是不確定的,也就是上面提到為什麼要特判 \(k=0\) 的情況。此時注意到區間 \(1 \sim i\) 是固定的,那麼按照預處理時的方式,在解決詢問時就可以得到所有 \(s_\) 的值(具體實現可以參考**)。此時直接列舉 \(j\) 更新答案,根據莫隊的性質,每次詢問的區間 \([l,r]\) 就是在離線莫隊時相鄰兩個詢問在乙個方向(左邊或右邊)相差的範圍,總的更新次數不會超過 \(o(n\sqrt m)\)。至此就得到了乙個複雜度為 \(o(c_^n+n\sqrt m)\) 的演算法。
最後,需要注意一下,在莫隊時記錄的答案只是與上乙個區間的差值,相當於差分陣列,所以還需要做一次字首和。同時,答案別忘了開** long long**。
#include#include#include#include#includeusing namespace std;
const int n=1e5+10;
#define ll long long
int g[n],f[n],len,n,m,num[n],k,tot,a[n]; ll ans[n],res[n];
struct nodeq[n];
struct twice;
vectorrange[n];
bool cmp(node x,node y)
int main()
for(int i=1,l=1,r=0;i<=m;i++)
); while(r>r) res[i]-=f[--r];
if(ll) range[r].push_back(twice);
while(l>l) res[i]-=f[l-2]+(!k),l--;
} memset(g,0,sizeof(g));
for(int i=1;i<=n;i++)
for(int i=2;i<=n;i++) res[i]+=res[i-1];
for(int i=1;i<=n;i++) ans[q[i].id]=res[i];
for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
return 0;
}
無刪除莫隊與二次離線莫隊
大佬部落格 無刪除莫隊 沒打過但是可以拿來湊數。不就是用可回退化 你一定需要看一看 十二省聯考2019 希望 的莫隊來。skip。二次離線莫隊 一開始看是ynoi的題以為沒有啥可拓展性。然後看了上面那位大佬的部落格才發現這個方法大有可為。莫隊實際上是把o m o m o m 個詢問拆分成o n m ...
洛谷P5906 模板 回滾莫隊 不刪除莫隊
莫隊好多套路啊。建議都學習一下 學習自題解 回滾莫隊的意思大概就是,按l所在塊再求列舉詢問,相同的一起求答案,l部分的必須每次從當前塊的最右邊向左拓展到想要的地方,求完當前詢問之後,你此時更新出的答案要回滾到l向左移動之前,再求下個詢問。左端點在乙個塊中的詢問處理完之後,我們清空所有最左和最右的數字...
莫隊 高階篇 二次離線莫隊與區間逆序對問題
一種高階科技,在noi2020day1t3 之後,不會 a 部分分 裸 區間逆序對 的餘下定決心要好好學習根號演算法。二次離線莫隊聽名字就是個非常高階的東西,而且雖說是二次離線,但它的複雜度仍舊是 o n sqrt n 非常神奇。一般用於移動左右端點時複雜度較大的莫隊。例如區間逆序對,它有乙個非常顯...