bzoj 3702: 二叉樹 (線段樹)
阿新 • • 發佈:2019-01-21
3702: 二叉樹
Time Limit: 15 Sec Memory Limit: 256 MBSubmit: 428 Solved: 184
[Submit][Status][Discuss]
Description
現在有一棵二叉樹,所有非葉子節點都有兩個孩子。在每個葉子節點上有一個權值(有n個葉子節點,滿足這些權值為1..n的一個排列)。可以任意交換每個非葉子節點的左右孩子。
要求進行一系列交換,使得最終所有葉子節點的權值按照中序遍歷寫出來,逆序對個數最少。
Input
第一行n
下面每行,一個數x
如果x==0,表示這個節點非葉子節點,遞迴地向下讀入其左孩子和右孩子的資訊,
如果x!=0,表示這個節點是葉子節點,權值為x。
Output
一行,最少逆序對個數。
Sample Input
30
0
3
1
2
Sample Output
1HINT
對於100%的資料:2<=n<=200000。
Source
題解:同bzoj 2212
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 400003 #define LL long long using namespace std; int n,m,val[N],rt,ls[N*20],rs[N*20],tot; int l[N],r[N],maxn,num[N],cnt,in[N],out[N],q[N],sz,root[N]; LL sl,sr,sum[N*20]; int dfs() { int now=++cnt; scanf("%d",&val[now]); if (val[now]) { q[++sz]=val[now]; in[now]=out[now]=sz; return now; } in[now]=sz+1; l[now]=dfs(); r[now]=dfs(); out[now]=sz; return now; } void update(int x) { sum[x]=sum[ls[x]]+sum[rs[x]]; } void insert(int &i,int l,int r,int x) { i=++tot; if (l==r) { sum[i]=1; return; } int mid=(l+r)/2; if (x<=mid) insert(ls[i],l,mid,x); else insert(rs[i],mid+1,r,x); update(i); } int merge(int x,int y) { if (!x) return y; if (!y) return x; sl+=sum[rs[x]]*sum[ls[y]]; sr+=sum[ls[x]]*sum[rs[y]]; ls[x]=merge(ls[x],ls[y]); rs[x]=merge(rs[x],rs[y]); update(x); return x; } LL solve(int x) { if (val[x]) return 0; LL ans=solve(l[x])+solve(r[x]); sl=0; sr=0; root[x]=merge(root[l[x]],root[r[x]]); return ans+min(sl,sr); } int main() { freopen("a.in","r",stdin); freopen("my.out","w",stdout); scanf("%d",&n); rt=dfs(); for (int i=1;i<=cnt;i++) if (val[i]) insert(root[i],1,n,val[i]); printf("%I64d\n",solve(rt)); }