题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2212
思路:用线段树合并求出交换左右儿子之前之后逆序对的数量,如果数量变小则交换.
实现代码:
#include<bits/stdc++.h> using namespace std; #define ll long long const int M = 4e5+10; int n,cnt,idx; ll ans,cnt1,cnt2; int v[M],l[M],r[M],root[M]; int sum[M*10],ls[M*10],rs[M*10]; void init_tree(int x){ scanf("%d",&v[x]); if(!v[x]){ l[x] = ++cnt; init_tree(l[x]); r[x] = ++cnt; init_tree(r[x]); } } void pushup(int rt){ sum[rt] = sum[ls[rt]] + sum[rs[rt]]; } void build(int p,int l,int r,int &rt){ if(!rt) rt = ++idx; if(l == r){ sum[rt] = 1; return ; } int mid = (l + r) >> 1; if(p <= mid) build(p,l,mid,ls[rt]); else build(p,mid+1,r,rs[rt]); pushup(rt); } int merge(int x,int y){ if(!x) return y; if(!y) return x; cnt1 += (ll)sum[rs[x]]*sum[ls[y]]; cnt2 += (ll)sum[ls[x]]*sum[rs[y]]; ls[x] = merge(ls[x],ls[y]); rs[x] = merge(rs[x],rs[y]); pushup(x); return x; } void solve(int x){ if(!x) return ; solve(l[x]); solve(r[x]); if(!v[x]){ cnt1 = cnt2 = 0; root[x] = merge(root[l[x]],root[r[x]]); ans += min(cnt1,cnt2); } } int main() { scanf("%d",&n); cnt = 1; init_tree(1); for(int i = 1;i <= cnt;i ++){ if(v[i]) build(v[i],1,n,root[i]); } solve(1); printf("%lld\n",ans); return 0; }
原文地址:https://www.cnblogs.com/kls123/p/9550227.html
时间: 2024-10-09 14:50:24