1.P1908 逆序对

动态开点线段树/离散化均可解决。

动态开点线段树维护桶,每次插值时统计值域上[线段树]打字练习2_c++的区间和,比其大的数的个数就是对总逆序对个数的贡献。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn = 5e5, maxx = 1e9 + 7;
int sum[maxn << 5], lid[maxn << 5], rid[maxn << 5], root, tot;

void push_up(int rt){
sum[rt] = sum[lid[rt]] + sum[rid[rt]];
}

void update(int &rt, int l, int r, int x, int value){
if(!rt) rt = ++tot;
if(l == r){
sum[rt] += value;
return;
}
int mid = l + r >> 1;
if(x <= mid) update(lid[rt], l, mid, x, value);
if(x > mid) update(rid[rt], mid + 1, r, x, value);
push_up(rt);
}

int query(int rt, int l, int r, int L, int R){
if(!rt) return 0;
if(l >= L && r <= R) return sum[rt];
int mid = l + r >> 1, ans = 0;
if(L <= mid) ans += query(lid[rt], l, mid, L, R);
if(R > mid) ans += query(rid[rt], mid + 1, r, L, R);
return ans;
}

signed main(){
int n = 0; cin >> n;
ll ans = 0;
for(int i = 1; i <= n; i++){
int x = 0; cin >> x;
ans += query(root, 1, maxx, x + 1, maxx);
update(root, 1, maxx, x, 1);
}
cout << ans << endl;
return 0;
}

2.P1637 三元上升子序列

题目要求在序列中求三元上升子序列的个数,先回忆一下逆序对计数的时候我们用的计数方法:建权值线段树动态插点统计。那么这个题可以采用类似的策略。

#include <bits/stdc++.h>
#define int unsigned long long
#define endl '\n'

const int N = 2e5 + 10;
int tree[N << 2], ls[N << 2], rs[N << 2], a[N], b[N], len;

namespace segt{
#define lson rt << 1, l,
#define rson rt << 1 | 1, mid + 1,
#define ls rt << 1
#define rs rt << 1 | 1
inline void push_up(int rt) { tree[rt] = tree[ls] + tree[rs]; }

static void update_point(int rt, int l, int r, int pos, int val){
if(l == r){
tree[rt] += val;
return;
}
int mid = l + r >> 1;
if(mid >= pos) update_point(lson, pos, val);
else update_point(rson, pos, val);
push_up(rt);
}

static int query(int rt, int l, int r, int ql, int qr){
if(l >= ql && r <= qr) return tree[rt];
int mid = l + r >> 1, ans = 0;
if(mid >= ql) ans += query(lson, ql, qr);
if(mid < qr) ans += query(rson, ql, qr);
return ans;
}
}

int smaller[N], bigger[N];

inline void solve(){
std::ios_base::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int n = 0, ans = 0; std::cin >> n;
for(int i = 1; i <= n; i++) std::cin >> a[i], b[i] = a[i];
std::sort(a + 1, a + 1 + n);
len = std::unique(a + 1, a + 1 + n) - a - 1;
auto query_pos = [&](int x) { return std::lower_bound(a + 1, a + 1 + len, x) - a; };
for(int i = 1; i <= n; i++){
segt::update_point(1, 1, n, query_pos(b[i]), 1);
int tmp = query_pos(b[i]);
if(tmp == 1) continue;
smaller[i] = segt::query(1, 1, n, 1, query_pos(b[i]) - 1);
}
memset(tree, 0, sizeof tree);
for(int i = n; i >= 1; i--){
segt::update_point(1, 1, n, query_pos(b[i]), 1);
bigger[i] = segt::query(1, 1, n, query_pos(b[i]) + 1, n);
}
for(int i = 1; i <= n; i++) ans += (smaller[i] * bigger[i]);
std::cout << ans << endl;
}

signed main(){
solve();
return 0;
}

3.P6186 [NOI Online #1 提高组] 冒泡排序

可以发现冒泡排序具有性质:如果记录每个点的逆序对对数,每轮冒泡排序结束,所有非零的逆序对数都会[线段树]打字练习2_#define_02。那么只需要维护一个区间和和一个区间点数和即可实现查询。

对于交换操作,首先将贡献从树上摘出来,然后分类讨论。对于[线段树]打字练习2_#define_03的情况,交换后会失去一个逆序对,而对于[线段树]打字练习2_i++_04的情况,则交换后会得到一个逆序对。同时由于是相邻数字的交换,因此交换操作不会影响其它点的逆序对数目。那么只需要讨论后再还原到树上即可。

可以全部用线段树,也可以全部用树状数组。且由于是[线段树]打字练习2_#define_05的排列,因此不需要考虑元素相等的情况,且不需要进行离散化。

#include <bits/stdc++.h>
#define int long long

const int N = 2e5 + 10;
int a[N], b[N], cnt[N];

namespace SegTree{
#define lson rt << 1, l,
#define rson rt << 1 | 1, mid + 1,
int tree[N << 2];

inline void push_up(int rt){ tree[rt] = tree[rt << 1] + tree[rt << 1 | 1]; }

static void update(int rt, int l, int r, int pos, int val){
if(l == r){
tree[rt] += val;
return;
}
int mid = l + r >> 1;
if(mid >= pos) update(lson, pos, val);
else update(rson, pos, val);
push_up(rt);
}

static int query(int rt, int l, int r, int L, int R){
if(l >= L && r <= R) return tree[rt];
int mid = l + r >> 1, ans = 0;
if(mid >= L) ans += query(lson, L, R);
if(mid < R) ans += query(rson, L, R);
return ans;
}
}

namespace BIT{
#define lowbit(x) ((x) & (-x))
struct tree_array{
int tree[N], len;

inline void init(int n){ len = n; }

inline void init(){
memset(tree, 0, sizeof(tree));
for(int i = 1, tmp = 0; i <= len; i++){
tree[i] += a[i];
tmp = i + lowbit(i);
if(tmp <= len) tree[tmp] += tree[i];
}
}

inline void update(int i, int x){
if(!i) return;
for(int pos = i; pos <= len; pos += lowbit(pos)) tree[pos] += x;
}

inline int getsum(int i, int ans = 0){
for(int pos = i; pos; pos -= lowbit(pos)) ans += tree[pos];
return ans;
}

inline int query(int l, int r){ return getsum(r) - getsum(l - 1); }
}bit[2];
void init(int n){ bit[0].init(n), bit[1].init(n); }
}

void solve(){
//std::ios_base::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int n, m; std::cin >> n >> m;
BIT::init(n);
for(int i = 1; i <= n; i++) std::cin >> a[i];
//auto query_pos = [&](int x){ return std::lower_bound(a + 1, a + 1 + len, x) - a; };
for(int i = 1; i <= n; i++){
SegTree::update(1, 1, n, a[i], 1);
cnt[i] = SegTree::query(1, 1, n, a[i] + 1, n);
BIT::bit[0].update(cnt[i], 1);
BIT::bit[1].update(cnt[i], cnt[i]);
}

for(int i = 1; i <= m; i++){
int op, x; std::cin >> op >> x;
if(op == 1){
BIT::bit[0].update(cnt[x], -1);
BIT::bit[1].update(cnt[x], -cnt[x]);
BIT::bit[0].update(cnt[x + 1], -1);
BIT::bit[1].update(cnt[x + 1], -cnt[x + 1]);
if(a[x] > a[x + 1]) --cnt[x + 1];
else ++cnt[x];
std::swap(cnt[x], cnt[x + 1]), std::swap(a[x], a[x + 1]);
BIT::bit[0].update(cnt[x], 1);
BIT::bit[1].update(cnt[x], cnt[x]);
BIT::bit[0].update(cnt[x + 1], 1);
BIT::bit[1].update(cnt[x + 1], cnt[x + 1]);
}
else if(op == 2){
if(x >= n){ puts("0"); continue; }
int ans1 = BIT::bit[0].query(x + 1, n);
int ans2 = BIT::bit[1].query(x + 1, n);
std::cout << ans2 - x * ans1 << std::endl;
}
}
}

signed main(){
solve();
return 0;
}

4.P3369【模板】普通平衡树

1.线段树解法

所有的操作都可以线段树乱搞。

#include <bits/stdc++.h>
#define endl '\n'
const int N = 1e6 + 10;

namespace SegTree{
#define lson rt << 1, l,
#define rson rt << 1 | 1, mid + 1,
int tree[N << 2];
inline void push_up(int rt){ tree[rt] = tree[rt << 1] + tree[rt << 1 | 1]; }

static void update(int rt, int l, int r, int pos, int val){
if(l == r){
tree[rt] += val;
return;
}
int mid = l + r >> 1;
if(mid >= pos) update(lson, pos, val);
else update(rson, pos, val);
push_up(rt);
}

static int query(int rt, int l, int r, int L, int R){
if(R < L) return 0;
if(l >= L && r <= R) return tree[rt];
int mid = l + r >> 1, ans = 0;
if(mid >= L) ans += query(lson, L, R);
if(mid < R) ans += query(rson, L, R);
return ans;
}

static int rank(int rt, int l, int r, int rk){
if(l == r) return l;
int mid = l + r >> 1;
if(tree[rt << 1] >= rk) return rank(lson, rk);
else return rank(rson, rk - tree[rt << 1]);
}
}//!SegmentTree

int opt[N], x[N], b[N], tot;

inline void solve(){
int n; std::cin >> n;
for(int i = 1; i <= n; i++){
std::cin >> opt[i] >> x[i];
if(opt[i] != 4) b[++tot] = x[i];
}
std::sort(b + 1, b + tot + 1);
for(int i = 1; i <= n; i++){
if(opt[i] != 4) x[i] = std::lower_bound(b + 1, b + tot + 1, x[i]) - b;
}
for(int i = 1; i <= n; i++){
if(opt[i] == 1) SegTree::update(1, 1, N, x[i], 1);
else if(opt[i] == 2) SegTree::update(1, 1, N, x[i], -1);
else if(opt[i] == 3) std::cout << SegTree::query(1, 1, N, 1, x[i] - 1) + 1 << endl;
else if(opt[i] == 4) std::cout << b[SegTree::rank(1, 1, N, x[i])] << endl;
else if(opt[i] == 5){
int rk = SegTree::query(1, 1, N, 1, x[i] - 1);
std::cout << b[SegTree::rank(1, 1, N, rk)] << endl;
}
else if(opt[i] == 6){
int rk = SegTree::query(1, 1, N, 1, x[i]);
std::cout << b[SegTree::rank(1, 1, N, rk + 1)] << endl;
}
}
}

signed main(){
solve();
return 0;
}

2.Treap正解

#include <bits/stdc++.h>
#define int long long
const int N = 2e5 + 10;

namespace treap{
struct Treap {
int lson[N], rson[N], val[N], rnd[N], size[N], w[N];
int tot, ans, root;

inline int Rand(){
static unsigned long long seed = 114514;
return (seed *= 23333ll) %= 998244553;
}

inline void push_up(int rt) { size[rt] = size[lson[rt]] + size[rson[rt]] + w[rt]; }

void lrotate(int &rt) {
int rpos = rson[rt];
rson[rt] = lson[rpos], lson[rpos] = rt;
size[rpos] = size[rt];
push_up(rt);
rt = rpos;
}

void rrotate(int &rt) {
int lpos = lson[rt];
lson[rt] = rson[lpos], rson[lpos] = rt;
size[lpos] = size[rt];
push_up(rt);
rt = lpos;
}

void insert(int &rt, int x) { // 插入
if (!rt) {
rt = ++tot;
size[rt] = w[rt] = 1;
val[rt] = x;
rnd[rt] = Rand();
return;
}
size[rt]++;
if (val[rt] == x) w[rt]++;
else if (val[rt] < x){
insert(rson[rt], x);
if (rnd[rson[rt]] < rnd[rt]) lrotate(rt);
}
else{
insert(lson[rt], x);
if (rnd[lson[rt]] < rnd[rt]) rrotate(rt);
}
}

bool del(int &rt, int x) { // 删除节点
if (!rt) return false;
if (val[rt] == x) {
if (w[rt] > 1) {
w[rt]--, size[rt]--;
return true;
}
if (lson[rt] == 0 || rson[rt] == 0) {
rt = lson[rt] + rson[rt];
return true;
}
else if (rnd[lson[rt]] < rnd[rson[rt]]) {
rrotate(rt);
return del(rt, x);
}
else {
lrotate(rt);
return del(rt, x);
}
}
else if (val[rt] < x) {
bool flag = del(rson[rt], x);
if (flag) size[rt]--;
return flag;
}
else {
bool flag = del(lson[rt], x);
if (flag) size[rt]--;
return flag;
}
}

int query_rank(int rt, int x) {
if (!rt) return 0;
if (val[rt] == x) return size[lson[rt]] + 1;
else if (x > val[rt]) return size[lson[rt]] + w[rt] + query_rank(rson[rt], x);
else return query_rank(lson[rt], x);
}

int query_num(int rt, int x) {
if (!rt) return 0;
if (x <= size[lson[rt]]) return query_num(lson[rt], x);
else if (x > size[lson[rt]] + w[rt]) return query_num(rson[rt], x - size[lson[rt]] - w[rt]);
else return val[rt];
}

void query_pre(int rt, int x) {
if (!rt) return;
if (val[rt] < x) ans = rt, query_pre(rson[rt], x);
else query_pre(lson[rt], x);
}

void query_nxt(int rt, int x) {
if (!rt) return;
if (val[rt] > x) ans = rt, query_nxt(lson[rt], x);
else query_nxt(rson[rt], x);
}

inline int get_pre(int rt, int x){
ans = 0;
query_pre(rt, x);
return val[ans];
}

inline int get_nxt(int rt, int x){
ans = 0;
query_nxt(rt, x);
return val[ans];
}
}T;

}

inline void solve(){
int n = 0; std::cin >> n;
for(int i = 1; i <= n; i++){
int opt, x; std::cin >> opt >> x;
if(opt == 1) treap::T.insert(treap::T.root, x);
else if(opt == 2) treap::T.del(treap::T.root, x);
else if(opt == 3) std::cout << treap::T.query_rank(treap::T.root, x) << std::endl;
else if(opt == 4) std::cout << treap::T.query_num(treap::T.root, x) << std::endl;
else if(opt == 5) std::cout << treap::T.get_pre(treap::T.root, x) << std::endl;
else if(opt == 6) std::cout << treap::T.get_nxt(treap::T.root, x) << std::endl;
}
}

signed main(){
solve();
return 0;
}