神奇的解法
对于每个节点,建出权值线段树
每次查询右子树的权值线段树和左子树的权值线段树,左子树中比右子树小的有多少?右子树比左子树小的有多少?(分别对应不交换的逆序对和交换的逆序对)
将左子树和右子树的权值线段树合并
递归进行这个操作
感觉复杂度很不靠谱,于是想证明一下复杂度
最开始权值线段树共O(nlogn)个节点,最后共O(n)个节点
每次合并两棵树的每个节点都要访问一遍,所以每个节点好像是要访问O(dep[i])次?
但是,合并两棵树后,有些重复的节点被合并到了一起
所以好像有些节点又没有合并O(dep[i])次?
感觉很靠谱,但是不会证明
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #define maxn 400010 #define N 4000100 using namespace std; int lch[N],rch[N],sum[N]; int root[maxn],l[maxn],r[maxn],w[maxn]; int n,m,num,tot; long long ans,cnt1,cnt2; void dfs(int x) { scanf("%d",&w[x]); if (!w[x]) { l[x]=++num; dfs(num); r[x]=++num; dfs(num); } } void update(int x) { sum[x]=sum[lch[x]]+sum[rch[x]]; } void modify(int &x,int l,int r,int d) { if (!x) x=++tot; if (l==r) {sum[x]=1;return;} int mid=(l+r)/2; if (d<=mid) modify(lch[x],l,mid,d); else modify(rch[x],mid+1,r,d); update(x); } int merge(int x,int y) { if (!x) return y; if (!y) return x; cnt1+=(long long)sum[rch[y]]*sum[lch[x]]; cnt2+=(long long)sum[rch[x]]*sum[lch[y]]; lch[x]=merge(lch[x],lch[y]); rch[x]=merge(rch[x],rch[y]); update(x); return x; } void dfs1(int x) { if (!w[x]) { dfs1(l[x]); dfs1(r[x]); cnt1=cnt2=0; root[x]=merge(root[l[x]],root[r[x]]); ans+=min(cnt1,cnt2); } } int main() { scanf("%d",&n); num++;dfs(1); for (int i=1;i<=num;i++) if (w[i]) modify(root[i],1,n,w[i]); dfs1(1); printf("%lld\n",ans); return 0; }
时间: 2024-08-02 11:11:05