题意:递归给出给一棵$n(1≤n≤200000)$个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少。

大体给出方式: 第一行一个正整数$n$,表示该二叉树的叶节点的个数;

下面若干行,每行一个数$p$:

如果$p=0$,表示这个节点不是叶节点,递归地向下读入其左孩子和右孩子的信息;

如果$p \neq 0$ ,表示这个节点是叶节点,权值为$p$ 。


本来想学一下启发式合并的,结果被一个很神奇的错误卡了很久。。

启发式合并的复杂度没怎么学会,只是大致知道权值线段树的合并和相同的节点数量成正相关,反正把只有一条链的权值线段树都合起来的复杂度是$O(nlogn)$的

不过在最后出现了一个神奇的错误

10分:

int dfs()
{
    scanf("%d",&k);
    if(k) return build(1,n,k);
    s1=s2=0;
    int now=Merge(dfs(),dfs());
    ans+=min(s1,s2);
    return now;
}

100分:

int dfs()
{
    scanf("%d",&k);
    if(k) return build(1,n,k);
    int now=Merge(dfs(),dfs());
    ans+=min(s1,s2);
    s1=s2=0;
    return now;
}

注意递归时赋初值该在什么时候搞


Code:

#include <cstdio>
#define ll long long
#define ls ch[now][0]
#define rs ch[now][1]
const int N=200000;
ll min(ll x,ll y){return x<y?x:y;}
int ch[N*25][2],n,k,tot;
ll sum[N*25],ans,s1,s2;
int build(int l,int r,int pos)
{
    int now=++tot;
    sum[now]++;
    if(l==r) return now;
    int mid=l+r>>1;
    if(pos<=mid)
        ls=build(l,mid,pos);
    else
        rs=build(mid+1,r,pos);
    return now;
}
int Merge(int x,int y)
{
    if(!x||!y) return x+y;
    sum[x]+=sum[y];
    s1+=sum[ch[x][1]]*sum[ch[y][0]];
    s2+=sum[ch[x][0]]*sum[ch[y][1]];
    ch[x][0]=Merge(ch[x][0],ch[y][0]);
    ch[x][1]=Merge(ch[x][1],ch[y][1]);
    return x;
}
int dfs()
{
    scanf("%d",&k);
    if(k) return build(1,n,k);
    int now=Merge(dfs(),dfs());
    ans+=min(s1,s2);
    s1=s2=0;
    return now;
}
int main()
{
    scanf("%d",&n);
    dfs();
    printf("%lld\n",ans);
    return 0;
}


2018.7.30

相关文章:

  • 2022-02-28
  • 2022-12-23
  • 2022-02-04
  • 2021-07-07
  • 2021-09-16
  • 2022-02-10
  • 2021-06-20
  • 2021-12-08
猜你喜欢
  • 2021-06-04
  • 2021-07-31
  • 2021-05-22
  • 2022-12-23
  • 2021-08-12
  • 2021-09-24
  • 2021-08-13
相关资源
相似解决方案