洛谷 P3521 [POI2011]ROT-Tree Rotations 解题报告

P3521 [POI2011]ROT-Tree Rotations

题意:递归给出给一棵\(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

posted @ 2018-07-30 18:23  露迭月  阅读(134)  评论(0编辑  收藏  举报