強化基礎演算法,提升個人水平。
#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;
}
}
}
}