您的位置:首页 > 其它

bzoj3702 二叉树

2017-01-26 21:15 176 查看
【题意】

一棵二叉树有n个叶子结点,每个叶子结点上有个权值,权值构成1~n的排列。每个非叶子结点均存在左右子树,可以交换每个非叶子结点的左右子树,使得权值按中序遍历写出后,逆序对个数最少。

【数据范围】

n<=200000

【思路】

每个结点独立处理

关键思路:启发式合并

整棵树从下到上,不断进行左右子树合并,如果枚举size较小的子树统计答案,则枚举复杂度为O(n log n)

[方法一]dfs序+主席树+每次枚举小区间

把每个子树看成区间,每次枚举小区间,查询大区间信息时使用主席树

[方法二]线段树合并

模板题,每次合并的同时统计答案,整个过程神似cdq分治

【时间复杂度】

[方法一]O(n log^2n)

[方法二]O(n log n)

【方法一】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 400010
#define ll long long
using namespace std;

struct pos{int l, r, mid;}pos
;
struct tt{int l, r, sum;}t[N*15];
int n, w
, ch
[2], fa
, nowfa, a
, root
, l, L, mid, R;
ll ans, s1, s2;

inline int read(){
int x=0, f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}

void dfs(int x){
if(!ch[x][0]){
a[++l]=w[x];
return;
}
pos[x].l=l+1;
dfs(ch[x][0]); pos[x].mid=l;
dfs(ch[x][1]); pos[x].r=l;
}

void ins(int i, int x, int L, int R){
t[l]=t[i]; t[l].sum++;
if(L<R){
int mid=(L+R)>>1, l1=l;
if(x<=mid){l++; t[l1].l=l; ins(t[i].l, x, L, mid);}
else{l++; t[l1].r=l; ins(t[i].r, x, mid+1, R);}
}
}
int c1(int x1, int x2, int x, int L, int R){
int sum=0, mid;
while(L<R){
mid=(L+R)>>1;
if(x<=mid){
x1=t[x1].l; x2=t[x2].l; R=mid;
}else{
sum+=t[t[x1].l].sum-t[t[x2].l].sum;
x1=t[x1].r; x2=t[x2].r; L=mid+1;
}
}
sum+=t[x1].sum-t[x2].sum;
return sum;
}
int c2(int x1, int x2, int x, int L, int R){
int sum=0, mid;
while(L<R){
mid=(L+R)>>1;
if(x<=mid){
sum+=t[t[x1].r].sum-t[t[x2].r].sum;
x1=t[x1].l; x2=t[x2].l; R=mid;
}else{
x1=t[x1].r; x2=t[x2].r; L=mid+1;
}
}
sum+=t[x1].sum-t[x2].sum;
return sum;
}

int main(){
n=read(); w[1]=read(); fa[1]=0;
memset(ch, 0, sizeof(ch));
nowfa=1;
for(int i=2; i<=n*2-1; i++){
w[i]=read(); fa[i]=nowfa;
if(!ch[nowfa][0])ch[nowfa][0]=i; else ch[nowfa][1]=i;
if(!w[i])nowfa=i;
else while(ch[nowfa][1])nowfa=fa[nowfa];
}
l=0; dfs(1);
l=root[0]=0;
for(int i=1; i<=n; i++){
l++; root[i]=l; ins(root[i-1], a[i], 1, n);
}
ans=0;
for(int i=1; i<=n*2-1; i++)if(!w[i]){
L=pos[i].l; mid=pos[i].mid; R=pos[i].r;
s1=s2=0;
if(mid-L+1<=R-mid){
for(int j=L; j<=mid; j++){
s1+=c1(root[R], root[mid], a[j], 1, n);
s2+=c2(root[R], root[mid], a[j], 1, n);
}
}else{
for(int j=mid+1; j<=R; j++){
s1+=c1(root[mid], root[L-1], a[j], 1, n);
s2+=c2(root[mid], root[L-1], a[j], 1, n);
}
}
ans+=min(s1, s2);
}
printf("%lld", ans);
return 0;
}
【方法二】

//线段树合并
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 400010
#define ll long long
using namespace std;

struct tt{int l, r, sum;}t[4000010];
int n, fa
, ch
[2], w
, nowfa, p, root
, l;
ll ans, s1, s2;

inline int read(){
int x=0, f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}

void ins(int i, int x, int L, int R){
t[i].sum++;
if(L<R){
int mid=(L+R)>>1;
if(x<=mid){
if(!t[i].l)t[i].l=++l;
ins(t[i].l, x, L, mid);
}else{
if(!t[i].r)t[i].r=++l;
ins(t[i].r, x, mid+1, R);
}
}
}

int merge(int x, int y){
if(!x)return y; if(!y)return x;
s1+=(ll)t[t[x].l].sum*t[t[y].r].sum;
s2+=(ll)t[t[x].r].sum*t[t[y].l].sum;
t[x].l=merge(t[x].l, t[y].l); t[x].r=merge(t[x].r, t[y].r);
t[x].sum+=t[y].sum;
return x;
}

void dfs(int x){
if(!w[x]){
dfs(ch[x][0]); dfs(ch[x][1]);
s1=s2=0; root[x]=merge(root[ch[x][0]], root[ch[x][1]]);
ans+=min(s1, s2);
}
}

int main(){
n=read()*2-1;
memset(ch, 0, sizeof(ch));
w[1]=read(); fa[1]=0; nowfa=1; p=0;
for(int i=2; i<=n; i++){
w[i]=read(); ch[nowfa][p]=i; fa[i]=nowfa;
if(w[i]){
while(ch[nowfa][1])nowfa=fa[nowfa];
p=1;
}else{nowfa=i; p=0;}
}
l=0; memset(root, 0, sizeof(root));
for(int i=1; i<=n; i++)if(w[i]){l++; root[i]=l; ins(l, w[i], 1, n/2+1);}
ans=0; dfs(1); printf("%lld", ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: