强基计划 题解
强化基础算法,提升个人水平。
#65. 跳树
考虑建线段树,对每个节点维护 rt, l, num
分别代表最高跳到哪层,跳到最高后最低到哪层,以及往下跳的路径(左儿子右儿子)。
然后合并时注意细节。
代码:
#include <bits/stdc++.h>
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
using namespace std;
struct d {
int rt, l, num;
d(int rt = 0, int l = 0, int num = 0):rt(rt), l(l), num(num) {}
d operator+(d o) {
if (!rt && !l) return o;
if (!o.rt && !o.l) return *this;
if (l > o.rt) {
d ans;
ans.rt = rt;
ans.l = l - o.rt + o.l;
ans.num = ((num >> o.rt) << o.l) + o.num;
return ans;
} else {
d ans;
ans.rt = rt + o.rt - l;
ans.l = o.l;
ans.num = o.num;
return ans;
}
}
d operator =(d o) {
rt = o.rt;
l = o.l;
num = o.num;
return *this;
}
}dat[2000010];
int opt[2000010], n, m, q, type, s, l, r, x, y;
d create(int opt) {
if (opt == 1) return d(0, 1, 0);
if (opt == 2) return d(0, 1, 1);
if (opt == 3) return d(1, 0, 0);
}
void build(int p, int l, int r) {
// cerr << p << " " << l << " " << r << "\n";
if (l == r) {
// cerr << p << "\n";
dat[p] = create(opt[l]);
// cerr << p << "\n";
} else {
int mid = (l + r) / 2;
build(2 * p, l, mid);
build(2 * p + 1, mid + 1, r);
dat[p] = dat[2 * p] + dat[2 * p + 1];
}
}
void modify(int p, int l, int r, int k) {
if (l == r) {
dat[p] = create(opt[l]);
} else {
int mid = (l + r) / 2;
if (k > mid) modify(2 * p + 1, mid + 1, r, k);
else modify(2 * p, l, mid, k);
dat[p] = dat[2 * p] + dat[2 * p + 1];
}
}
d query(int p, int cl, int cr, int l, int r) {
// cerr << p << " " << cl << " " << cr << " " << l << " " << r << "\n";
if (l >= cl && r <= cr) {
return dat[p];
} else if (l > cr || r < cl) {
return d();
} else {
int mid = (l + r) / 2;
return query(2 * p, cl, cr, l, mid) + query(2 * p + 1, cl, cr, mid + 1, r);
}
}
signed main() {
// cerr << "here" << "\n";
cin >> n >> m >> q;
// cerr << "here" << "\n";
rep (i, 1, m) {
cin >> opt[i];
}
// cerr << "here" << "\n";
build(1, 1, m);
// cerr << "here" << "\n";
rep (i, 1, q) {
// cerr << i << "\n";
cin >> type;
if (type == 1) {
cin >> s >> l >> r;
d k = query(1, l, r, 1, m);
cout << (max(1ll, s >> k.rt) << k.l) + k.num << "\n";
} else {
cin >> x >> y;
opt[x] = y;
modify(1, 1, m, x);
}
}
}
#66. [NOI Online #1 提高组] 冒泡排序
考虑记录对于一个数,前面比它大的数有几个。
然后一轮冒泡排序中,设对于某一个数,前面比它大的数的个数为 \(0\) 的数有 \(x\) 个,那么这一轮会消掉 \(n - x\) 个逆序对。
随后离散化后树状数组即可。
代码:
#include <bits/stdc++.h>
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
using namespace std;
int p[200010], v[200010], n, m, opt, c, sum;
struct BIT {
int tr[200010];
BIT() {}
int lowbit(int i) { return i & -i; }
void add(int x, int d) {
x++;
while (x) {
tr[x] += d;
x -= lowbit(x);
}
}
int query(int x) {
x++;
int res = 0;
while (x <= n) {
res += tr[x];
x += lowbit(x);
}
return res;
}
int queryPoint(int x) {
return query(x) - query(x - 1);
}
}tr1, tr2, tr3;
signed main() {
int cnt = 0;
cin >> n >> m;
rep (i, 1, n) {
cin >> p[i];
sum += p[i];
int x = tr3.query(p[i]);
v[i] = x;
tr2.add(x, v[i]);
tr1.add(x, 1);
tr3.add(p[i], 1);
}
rep (i, 1, m) {
// cerr << i << "\n";
// rep (j, 1, n) {
// cerr << v[j] << " ";
// }
// cerr << "\n";
cin >> opt >> c;
if (opt == 1) {
tr2.add(v[c], -v[c]);
tr2.add(v[c + 1], -v[c + 1]);
tr1.add(v[c], -1);
tr1.add(v[c + 1], -1);
swap(v[c], v[c + 1]);
if (p[c] < p[c + 1]) {
v[c + 1]++;
} else {
v[c]--;
}
swap(p[c], p[c + 1]);
tr2.add(v[c], v[c]);
tr2.add(v[c + 1], v[c + 1]);
tr1.add(v[c], 1);
tr1.add(v[c + 1], 1);
} else {
int k = c;
// k++;
if (k >= n - 1) {
cout << 0 << endl;
} else {
cout << tr2.query(k) - tr1.query(k) * k << endl;
}
}
}
}