莫队算法
莫队
普通莫队
注意顺序正确!!
codes
for (int i = 1; i <= m; i++) {
int pl = q[i].l, pr = q[i].r;
while (l > pl) add(a[--l]);
while (r < pr) add(a[++r]);
while (l < pl) del(a[l++]);
while (r > pr) del(a[r--]);
ans[q[i].id] = cal(q[i].R) - cal(q[i].L - 1);
}
值域二分
https://acm.hdu.edu.cn/showproblem.php?pid=6959
\(O(1)\)修改,\(O(sqrt(m))\)查询,在n,m等阶的情况下 ,复杂度平均只有\(O(n*sqrt(n)+ m*sqrt(n))\)比线段树等数据结构维护O(long(n))地修改查询优.
upd: 多组输入注意memset..
codes
const int maxn = 1e5 + 7;
int n, m, t, k = sqrt(maxn), a[maxn], num[maxn], sum[maxn], ans[maxn];
struct query {
int l, r, L, R, id;
} q[maxn];
bool cmp(query a, query b) {
if (a.l / k != b.l / k)
return a.l / k < b.l / k;
if ((a.l / k) & 1)
return a.r < b.r;
else
return a.r > b.r;
}
void add(int x) {
num[x]++;
if (num[x] == 1)
sum[x / k]++;
}
void del(int x) {
num[x]--;
if (num[x] == 0)
sum[x / k]--;
}
int cal(int x) {
int res = 0;
for (int i = 0; i < x / k; i++)
res += sum[i];
for (int i = (x / k) * k; i <= x; i++)
res += (num[i] >= 1);
return res;
}
void solve() {
cin >> t;
while (t--) {
memset(num, 0, sizeof num);
memset(sum, 0, sizeof sum);
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++)
cin >> q[i].l >> q[i].L >> q[i].r >> q[i].R, q[i].id = i;
sort(q + 1, q + 1 + m, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; i++) {
int pl = q[i].l, pr = q[i].r;
while (l > pl) add(a[--l]);
while (r < pr) add(a[++r]);
while (l < pl) del(a[l++]);
while (r > pr) del(a[r--]);
ans[q[i].id] = cal(q[i].R) - cal(q[i].L - 1);
}
for (int i = 1; i <= m; i++)
cout << ans[i] << "\n";
}
}
带修莫队
-
修改记录时间,修改值,修改后值 同时读入修改时进行修改,修改结束后还原
-
分块大小:\(pow(n,2.0/3)\)
codes
const int maxn = 2e6 + 7;
int n, t, qn, m, k, a[maxn], ans[maxn], num[maxn], sum;
char op;
struct query {
int l, r, t, id;
} q[maxn];
struct change {
int pl, val, oval;
} chg[maxn];
bool cmp(query a, query b) {
if (a.l / k != b.l / k) return a.l / k < b.l / k;
if (a.r / k != b.r / k) return a.r / k < b.r / k;
return a.t < b.t;
}
void add(int x) {
if (x == 0)
return;
num[x]++;
sum += (num[x] == 1);
}
void del(int x) {
if (x == 0)
return;
num[x]--;
sum -= (num[x] == 0);
}
int l = 1, r = 0, tn = 0;
void addchange(int t) {
int pl = chg[t].pl, val = chg[t].val;
if (pl >= l && pl <= r)
add(val), del(a[pl]);
a[pl] = val;
}
void delchange(int t) {
int pl = chg[t].pl, val = chg[t].oval;
if (pl >= l && pl <= r)
add(val), del(a[pl]);
a[pl] = val;
}
void solve() {
cin >> n >> m;
k = pow(n, 2.0 / 3);
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1, l, r; i <= m; i++) {
cin >> op >> l >> r;
if (op == 'R') chg[++t].pl = l, chg[t].val = r, chg[t].oval = a[l], a[l] = r;
else q[++qn].l = l, q[qn].r = r, q[qn].t = t, q[qn].id = qn;
}
for (int i = t; i >= 1; i--) a[chg[i].pl] = chg[i].oval;
sort(q + 1, q + 1 + qn, cmp);
for (int i = 1; i <= qn; i++) {
int L = q[i].l, R = q[i].r, T = q[i].t;
while (tn < T) addchange(++tn);
while (tn > T) delchange(tn--);
while (l > L) add(a[--l]);
while (r < R) add(a[++r]);
while (l < L) del(a[l++]);
while (r > R) del(a[r--]);
ans[q[i].id] = sum;
}
for (int i = 1; i <= qn; i++)
cout << ans[i] << "\n";
}
- 修改添加和删除的代码很重要..
- 可以树状数组写..
codes
const int maxn = 1e5 + 7;
int n, t, m, k, l, r;
char s[maxn], ch;
int ans[maxn], sum, num[200];
int qn;
struct query {
int l, r, id, t;
} q[maxn];
int tn;
struct change {
int pl;
char val, oval;
} c[maxn];
bool cmp(query a, query b) {
if (a.l / k != b.l / k) return a.l / k < b.l / k;
if (a.r / k != b.r / k) return a.r / k < b.r / k;
return a.t < b.t;
}
void add(char ch) {
num[ch]++;
if (num[ch] == 1)
sum++;
}
void del(char ch) {
num[ch]--;
if (num[ch] == 0)
sum--;
}
void addchange(int t) {
int pl = c[t].pl, val = c[t].val;
if (pl >= l && pl <= r)
add(val), del(s[pl]);
s[pl] = val;
}
void delchange(int t) {
int pl = c[t].pl, val = c[t].oval;
if (pl >= l && pl <= r)
add(val), del(s[pl]);
s[pl] = val;
}
void solve() {
cin >> (s + 1) >> m;
k = pow(strlen(s + 1), 2.0 / 3);
for (int i = 1; i <= m; i++) {
cin >> n;
if (n == 1) {
cin >> n >> ch;
c[++tn].pl = n, c[tn].val = ch, c[tn].oval = s[n];
s[n] = ch;
} else {
cin >> l >> r;
qn++, q[qn] = {l, r, qn, tn};
}
}
for (int i = tn; i >= 1; i--)
s[c[i].pl] = c[i].oval;
sort(q + 1, q + 1 + qn, cmp);
l = 1, r = 0, t = 0;
for (int i = 1; i <= qn; i++) {
int pl = q[i].l, pr = q[i].r, pt = q[i].t;
while (t < pt) addchange(++t);
while (t > pt) delchange(t--);
while (l > pl) add(s[--l]);
while (r < pr) add(s[++r]);
while (l < pl) del(s[l++]);
while (r > pr) del(s[r--]);
ans[q[i].id] = sum;
}
for (int i = 1; i <= qn; i++)
cout << ans[i] << endl;
}
树上莫队
伪树上莫队
树剖下加,对每次询问拆出来就行了..
codes
真树上莫队
codes
回滚莫队
莫队配合bitset
- 离散化时lower_bound可以记录下上一个数到多少位
- 用bitset维护相同的数的个数时,如果个数最大是固定的,可以记录一个cnt[]数组,来记录当前数用到了第多少位
- 空间开不下时,可以对询问分块处理
codes
const int maxn = 1e5 + 7;
int n, t, m;
int a[maxn], b[maxn];
int cnt = 0;
set<int> st;
map<int, int> mp;
int k;
struct query {
int id, l, r;
} q[maxn];
bool cmp(query a, query b) {
if (a.l / k == b.l / k)
return a.r < b.r;
return a.l < b.l;
}
bitset<maxn> bst[maxn / 3];
bitset<maxn> now;
int ans[maxn], pl[maxn];
void add(int x) {
now.set(x + pl[x]);
pl[x]++;
}
void del(int x) {
pl[x]--;
now.reset(x + pl[x]);
}
void solve(int sl, int sr) {
for (int i = 0; i <= sr - sl + 1; i++)
bst[i].set();
memset(pl, 0, sizeof pl);
now.reset();
int tot = 0;
for (int i = sl, l, r; i <= sr; i++) {
for (int j = 1; j <= 3; j++) {
cin >> l >> r;
ans[i] += (r - l + 1);
q[tot++] = {i - sl + 1, l, r};
}
}
sort(q, q + tot, cmp);
int l = 1, r = 0;
for (int i = 0; i < tot; i++) {
int pl = q[i].l, pr = q[i].r;
while (l > pl) add(a[--l]);
while (r < pr) add(a[++r]);
while (l < pl) del(a[l++]);
while (r > pr) del(a[r--]);
bst[q[i].id] &= now;
}
for (int i = sl; i <= sr; i++)
ans[i] -= bst[i - sl + 1].count() * 3;
}
void solve() {
cin >> n >> m;
k = sqrt(n) + 1;
for (int i = 1; i <= n; i++)
cin >> a[i], b[i] = a[i];
sort(b + 1, b + 1 + n);
for (int i = 1; i <= n; i++)
a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b;
solve(1, m / 3);
solve(m / 3 + 1, m / 3 * 2);
solve(m / 3 * 2 + 1, m);
for (int i = 1; i <= m; i++)
cout << ans[i] << "\n";
}
- a - b = x 可以转换成 a = b + x.用bitset记录区间数的种类,存在a - b = x 等价于 bitset & bitset<<x 是否有1
- a + b = x 可以记b = N - B,也就是判断 a - B = x - N是否存在,另外记录一个bitset就能转化到两数之差
- a * b = x 直接枚举因子即可
codes
const int maxn = 1e5 + 7;
int n, t, m, k;
int a[maxn], ans[maxn];
struct query {
int id, op, x, l, r;
} q[maxn];
bool cmp(query a, query b) {
if (a.l / k == b.l / k)
return a.r < b.r;
return a.l / k < b.l / k;
}
bitset<maxn> now, eth;
int cnt[maxn], nct[maxn];
void add(int x) {
cnt[x]++, nct[maxn - x]++;
now.set(x);
eth.set(maxn - x);
}
void del(int x) {
cnt[x]--, nct[maxn - x]--;
if (cnt[x] == 0)
now.reset(x);
if (nct[maxn - x] == 0)
eth.reset(maxn - x);
}
void solve() {
cin >> n >> m;
k = (int) sqrt(maxn);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++) {
cin >> q[i].op >> q[i].l >> q[i].r >> q[i].x;
q[i].id = i;
}
sort(q + 1, q + 1 + m, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; i++) {
int pl = q[i].l, pr = q[i].r;
while (l > pl) add(a[--l]);
while (r < pr) add(a[++r]);
while (l < pl) del(a[l++]);
while (r > pr) del(a[r--]);
if (q[i].op == 1) {
ans[q[i].id] = (int) (now & (now << q[i].x)).any();
} else if (q[i].op == 2) {
ans[q[i].id] = (int) (now & (eth >> (maxn - q[i].x))).any();
} else {
for (int c = 1; c * c <= q[i].x; c++) {
if (q[i].x % c) continue;
if (now[c] && now[q[i].x / c])
ans[q[i].id]++;
}
}
}
for (int i = 1; i <= m; i++)
if (ans[i]) cout << "hana" << endl;
else cout << "bi" << endl;
}
多了个除法的处理,对于小于k的,维护下以每个点为右端点时,左端点最右能到达的地方
codes
const int maxn = 1e5 + 7;
const int k = 310;
int n, t, m;
int a[maxn], ans[maxn];
struct query {
int id, op, x, l, r;
} q[maxn];
bool cmp(query a, query b) {
if (a.l / k == b.l / k)
return a.r < b.r;
return a.l / k < b.l / k;
}
bitset<maxn> now, eth;
int cnt[maxn], nct[maxn];
void add(int x) {
cnt[x]++, nct[maxn - x]++;
now.set(x);
eth.set(maxn - x);
}
void del(int x) {
cnt[x]--, nct[maxn - x]--;
if (cnt[x] == 0)
now.reset(x);
if (nct[maxn - x] == 0)
eth.reset(maxn - x);
}
int mxl[k][maxn], lst[maxn];
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int x = 1; x < k; x++) {
memset(lst, 0, sizeof lst);
for (int i = 1; i <= n; i++) {
lst[a[i]] = i;
mxl[x][i] = mxl[x][i - 1];
if (a[i] % x == 0 && lst[a[i] / x])
mxl[x][i] = max(mxl[x][i], lst[a[i] / x]);
if (a[i] * x < maxn && lst[a[i] * x])
mxl[x][i] = max(mxl[x][i], lst[a[i] * x]);
}
}
for (int i = 1; i <= m; i++) {
cin >> q[i].op >> q[i].l >> q[i].r >> q[i].x;
q[i].id = i;
}
sort(q + 1, q + 1 + m, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; i++) {
int pl = q[i].l, pr = q[i].r;
while (l > pl) add(a[--l]);
while (r < pr) add(a[++r]);
while (l < pl) del(a[l++]);
while (r > pr) del(a[r--]);
if (q[i].op == 1) {
ans[q[i].id] = (int) (now & (now << q[i].x)).any();
} else if (q[i].op == 2) {
ans[q[i].id] = (int) (now & (eth >> (maxn - q[i].x))).any();
} else if (q[i].op == 3) {
for (int c = 1; c * c <= q[i].x; c++) {
if (q[i].x % c) continue;
if (now[c] && now[q[i].x / c])
ans[q[i].id]++;
}
} else if (q[i].op == 4) {
if (q[i].x >= k) {
for (int c = 1; c * q[i].x < maxn; c++)
if (now[c] && now[c * q[i].x]) {
ans[q[i].id]++;
break;
}
} else {
if (q[i].x == 0) continue;
if (mxl[q[i].x][q[i].r] >= q[i].l)
ans[q[i].id]++;
}
}
}
for (int i = 1; i <= m; i++)
if (ans[i]) cout << "yuno" << endl;
else cout << "yumi" << endl;
}
我看见 你