首页 > 代码库 > 校内训练0531 逆序对
校内训练0531 逆序对
【题目大意】
有一棵2n-1个节点的二叉树,它有恰好n个叶子节点,每个叶子节点上写了一个整数。如果将这棵树的所有叶子节点上的数从左到右写下来,便得到一个序列a[1]…a[n]。现在想让这个序列中的逆序对数量最少,但唯一的操作就是选树上一个非叶子节点,将它的左右两颗子树交换。你可以做任意多次这个操作。求在最优方案下,该序列的逆序对数最少有多少。
n<=200000
【题解】
啊很明显我们对于每个节点 判断一下两边交换/不交换哪个逆序对贡献的少就行了
至于这个逆序对贡献啊?线段树合并!
啊我不会线段树合并啊?启发式合并(dsu on tree)
# include <vector> # include <stdio.h> # include <assert.h> # include <string.h> # include <iostream> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10; const int mod = 1e9+7; # define RG register # define ST static int n, siz, rt, ch[M][2], siz2; int val[M], a[M], m, sz[M], sz2[M]; vector<int> ps; struct BIT { int c[M], n; # define lb(x) (x&(-x)) inline void set(int _n) { n = _n; memset(c, 0, sizeof c); } inline void edt(int x, int d) { for (; x<=n; x+=lb(x)) c[x] += d; } inline int sum(int x) { int ret=0; for (; x; x-=lb(x)) ret += c[x]; return ret; } inline int sum(int l, int r) { if(l>r) return 0; return sum(r)-sum(l-1); } }T; inline void dfs(int &x) { int t; scanf("%d", &t); if(t == 0) x = ++siz; else x = ++siz2; sz[x] = 1; if(t == 0) { dfs(ch[x][0]); dfs(ch[x][1]); sz[x] += sz[ch[x][0]] + sz[ch[x][1]]; sz2[x] = sz2[ch[x][0]] + sz2[ch[x][1]]; } else { sz2[x] = 1; val[x] = t; ps.push_back(t); } } bool big[M]; ll cura = 0, curb; inline void calc(int x) { if(val[x] != 0) { cura += T.sum(val[x]+1, n); curb += T.sum(val[x]-1); return ; } if(!big[ch[x][0]]) calc(ch[x][0]); if(!big[ch[x][1]]) calc(ch[x][1]); } inline void del(int x, int d) { if(x <= n) { T.edt(val[x], d); return ; } if(!big[ch[x][0]]) del(ch[x][0], d); if(!big[ch[x][1]]) del(ch[x][1], d); } ll ans = 0; inline void dsu(int x, bool kep) { if(x <= n) { if(kep) T.edt(val[x], 1); return; } int bc, sc; if(sz[ch[x][0]] > sz[ch[x][1]]) bc = ch[x][0], sc = ch[x][1]; else bc = ch[x][1], sc = ch[x][0]; dsu(sc, 0); dsu(bc, 1); // printf("x=%d, query=%d\n", x, T.sum(1, n)); big[bc] = 1; cura = curb = 0; calc(x); del(x,1); ans += min(cura, curb); big[bc] = 0; if(kep == 0) del(x,-1); } int main() { cin >> n; siz = n; dfs(rt); T.set(n); sort(ps.begin(), ps.end()); ps.erase(unique(ps.begin(), ps.end()), ps.end()); for (int i=1; i<=siz; ++i) if(val[i] != 0) val[i] = lower_bound(ps.begin(), ps.end(), val[i]) - ps.begin() + 1; // for (int i=1; i<=siz; ++i) printf("x=%d, ls=%d, rs=%d\n", i, ch[i][0], ch[i][1]); dsu(rt, 1); cout << ans << endl; return 0; }
校内训练0531 逆序对
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。