1. 程式人生 > >洛谷 P3521 [POI2011]ROT-Tree Rotations 解題報告

洛谷 P3521 [POI2011]ROT-Tree Rotations 解題報告

方式 遞歸 sum 合並 += line 相關 ota 報告

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

洛谷 P3521 [POI2011]ROT-Tree Rotations 解題報告