帶修莫隊
- 允許離線的帶區間修改的操作
-將修改操作編號,稱爲"時間戳"
普通莫隊是不能帶修改的,我們可以強行讓它可以修改
可以強行加上一維時間維, 表示這次操作的時間
排序
形如普通莫隊
struct Node {
int left;
int right;
int time;
int id;
friend bool operator <(const Node& a, const Node& b) {
return belong[a.left] ^ belong[b.left] ? belong[a.left] < belong[b.left] :
//先按左區間爲第一關鍵字排序
belong[a.right] ^ belong[b.right] ? belong[a.right] < belong[b.right] :
//再按右區間爲第二關鍵字排序
a.time < b.time;
//最後按時間排序
}
}q[maxn];
複雜度
一般用的分塊大小爲
可以用和普通莫隊類似的方法排序轉移,做到
這一次我們排序的方式是以 爲一塊,分成了 塊
第一關鍵字是左端點所在塊,第二關鍵字是右端點所在塊,第三關鍵字是時間。
還是來證明一下時間複雜度(默認塊大小爲):
- 左右端點所在塊不變,時間在排序後單調向右移,這樣的複雜度是
- 若左右端點所在塊改變,時間一次最多會移動個格子,時間複雜度
- 左端點所在塊一共有 中,右端點也是` 種
- 一共 種,每種乘上移動的複雜度 $O(n) $,總複雜度
代碼
Node
增加時間戳
int block, belong[maxn];
struct Node {
int left;
int right;
int time; //增加時間維
int id;
friend bool operator <(const Node& a, const Node& b) {
return belong[a.left] ^ belong[b.left] ? belong[a.left] < belong[b.left] :
belong[a.right] ^ belong[b.right] ? belong[a.right] < belong[b.right] :
a.time < b.time;
}
}q[maxn];
讀入
一定要另開一個數組模擬改變的過程
for (int i = 1; i <= m; i++) {
scanf("%s", p);
if (p[0] == 'Q') {
q[++cnt].left = read();
q[cnt].right = read();
q[cnt].id = cnt;
q[cnt].time = t;
}
else { //設b數組存改變後的值,存Modify裏
modify[++t].position = read();
modify[t].now = read();
modify[t].pre = b[modify[t].position];
b[modify[t].position] = modify[t].now;
}
}
莫隊
register int stdl = 1, stdr = 0, stdt = 0, ans = 0, position;
//左指針、右指針、時間指針
for (int i = 1; i <= cnt; i++) {
while (stdl > q[i].left) {
stdl--;
//operation 添加左端點
}
while (stdr < q[i].right) {
stdr++;
//operation 添加右端點
}
while (stdl < q[i].left) {
//operation 刪除左端點
stdl++;
}
while (stdr > q[i].right) {
//operation 刪除右端點
stdr--;
}
while (stdt < q[i].time) {
stdt++;
position = modify[stdt].position;
//operation 修改位置
if (position >= stdl && position <= stdr) {
//operation 刪除原貢獻
//operation 增加新貢獻
}
}
while (stdt > q[i].time) {
position = modify[stdt].position;
//operation 變回原來
if (position >= stdl && position <= stdr) {
//operation 刪除新貢獻
//operation 增加原貢獻
}
stdt--;
}
res[q[i].id] = ans;
}
模板題
P1903 數顏色 / 維護隊列
Game
題意
你有堆石子,每堆石子有個石子
遊戲規則:
-
Alice 先選擇一個大範圍區間內的石子
-
Bob選擇一個子區間內的石子最終進行遊戲
-
每次至少取走某一堆的一個石子,至多全部取走,無法移動石子者輸(Nim 博弈)
Alice先手,雙方足夠聰明
問對Alice的每次選擇,Bob有多少種選擇能讓Alice必勝
-
修改操作,即交換相鄰的兩堆石子
思路
Nim博弈
易知爲他們進行的遊戲爲Nim博弈
要使Alice獲勝,只需要區間異或和不爲0即可
那麼我們就可以去求他的補集,區間異或和爲0的情況
帶修莫隊
- 數據範圍爲1e5,時間10s,可以接受帶修莫隊
- 單點修改,查詢區間異或相同的個數,均可以由帶修莫隊實現
- 區間異或和爲0的個數即爲相同的前綴異或和對數
因爲計算前綴和的對數,所以要 left - 1,腦模一下就知道了 - 單點修改造成影響只對前一個的前綴和產生影響
代碼
時間戳
struct Time {
int x; //修改位置
int pre; //原值
int now; //新值
}t[maxn];
離線詢問
int block, belong[maxn];
struct Node {
int left;
int right;
int time;
int id;
friend bool operator <(const Node& a, const Node& b) {
return belong[a.left] == belong[b.left] ?(belong[a.right] == belong[b.right] ?
a.time < b.time : //time爲第三關鍵字
a.right < b.right) : //right爲第二關鍵字
a.left < b.left; //left爲第一關鍵字
}
}q[maxn];
預處理
block = (int)pow(n, 2. / 3); //帶修莫隊,常用block
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b[i] = a[i];
belong[i] = i / block; //預處理block
sum[i] = sum[i - 1] ^ a[i];//前綴和
r_sum[i] = sum[i]; //模擬前綴和
}
int cnt = 0, opt, T = 0;
for (int i = 1; i <= m; i++) {
scanf("%d", &opt);
if (opt == 1) {
scanf("%d%d", &q[++cnt].left, &q[cnt].right);
q[cnt].left--;
q[cnt].time = T;
q[cnt].id = cnt;
}
else {
scanf("%d", &t[++T].x); //在b[]和r_sum[]模擬修改,記錄
t[T].pre = r_sum[t[T].x];
t[T].now = r_sum[t[T].x] = r_sum[t[T].x + 1] ^ b[t[T].x];
swap(b[t[T].x], b[t[T].x + 1]);
}
}
sort(q + 1, q + 1 + cnt);
莫隊
int stdl = 1, stdr = 0, stdt = 0; LL ans = 0;
register int position;
for (int i = 1; i <= cnt; i++) {
while (stdl > q[i].left) ans = ans + col[sum[--stdl]]++;
while (stdl < q[i].left) ans = ans - --col[sum[stdl++]];
while (stdr < q[i].right) ans = ans + col[sum[++stdr]]++;
while (stdr > q[i].right) ans = ans - --col[sum[stdr--]];
while (stdt < q[i].time) {
position = t[++stdt].x;
sum[position] = t[stdt].now; //更改值
if (position >= q[i].left && position <= q[i].right) {
ans = ans - --col[t[stdt].pre]; //修改貢獻
ans = ans + col[t[stdt].now]++;
}
}
while (stdt > q[i].time) {
position = t[stdt].x;
sum[position] = t[stdt].pre; //更改值
if (position >= q[i].left && position <= q[i].right) {
ans = ans - --col[t[stdt].now];//修改貢獻
ans = ans + col[t[stdt].pre]++;
}
stdt--;
}
res[q[i].id] = LL(q[i].right - q[i].left + 1) * (q[i].right - q[i].left) / 2 - ans; //取補集
}
AC
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 5;
const int maxm = 1e6 + 5e4 + 5;
struct Time {
int x;
int pre;
int now;
}t[maxn];
int block, belong[maxn];
struct Node {
int left;
int right;
int time;
int id;
friend bool operator <(const Node& a, const Node& b) {
return belong[a.left] == belong[b.left] ?
(belong[a.right] == belong[b.right] ? a.time < b.time : a.right < b.right) :
a.left < b.left;
}
}q[maxn];
int a[maxn], b[maxn], sum[maxn], r_sum[maxn];
int col[maxm];
LL res[maxn];
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
memset(col, 0, sizeof(col));
block = (int)pow(n, 2. / 3);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]); b[i] = a[i];
belong[i] = i / block;
sum[i] = sum[i - 1] ^ a[i];
r_sum[i] = sum[i];
}
int cnt = 0, opt, T = 0;
for (int i = 1; i <= m; i++) {
scanf("%d", &opt);
if (opt == 1) {
scanf("%d%d", &q[++cnt].left, &q[cnt].right);
q[cnt].left--;
q[cnt].time = T;
q[cnt].id = cnt;
}
else {
scanf("%d", &t[++T].x);
t[T].pre = r_sum[t[T].x];
t[T].now = r_sum[t[T].x] = r_sum[t[T].x + 1] ^ b[t[T].x];
swap(b[t[T].x], b[t[T].x + 1]);
}
}
sort(q + 1, q + 1 + cnt);
int stdl = 1, stdr = 0, stdt = 0; LL ans = 0;
register int position;
for (int i = 1; i <= cnt; i++) {
while (stdl > q[i].left) ans = ans + col[sum[--stdl]]++;
while (stdl < q[i].left) ans = ans - --col[sum[stdl++]];
while (stdr < q[i].right) ans = ans + col[sum[++stdr]]++;
while (stdr > q[i].right) ans = ans - --col[sum[stdr--]];
while (stdt < q[i].time) {
position = t[++stdt].x;
sum[position] = t[stdt].now;
if (position >= q[i].left && position <= q[i].right) {
ans = ans - --col[t[stdt].pre];
ans = ans + col[t[stdt].now]++;
}
}
while (stdt > q[i].time) {
position = t[stdt].x;
sum[position] = t[stdt].pre;
if (position >= q[i].left && position <= q[i].right) {
ans = ans - --col[t[stdt].now];
ans = ans + col[t[stdt].pre]++;
}
stdt--;
}
res[q[i].id] = LL(q[i].right - q[i].left + 1) * (q[i].right - q[i].left) / 2 - ans;
}
for (int i = 1; i <= cnt; i++)
printf("%lld\n", res[i]);
}
}