题解 洛谷 P3521 【[POI2011]ROT-Tree Rotations】

给定一棵二叉树,叶子节点有权值,可以进行若干次交换一个节点的左右儿子的操作,使前序遍历叶子的逆序对最少。

考虑一个节点下子树逆序对的产生:

① 只在左子树中产生。

② 只在右子树中产生。

③ 在左子树和右子树中交叉产生。

因为二叉树的性质,所以 ① ② 两种情况只需递归下去求解,只需考虑情况 ③ 。

用权值线段树来记录信息,通过线段树合并来统计答案。

具体实现细节看代码吧。

记得开\(long\ long\)

\(code:\)

#include<bits/stdc++.h>
#define maxn 10000010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,root,tree_cnt;
ll s1,s2,ans;
bool vis[maxn];
int ls[maxn],rs[maxn],rt[maxn];
ll siz[maxn];
void pushup(int cur)
{
    siz[cur]=siz[ls[cur]]+siz[rs[cur]];
}
void modify(int L,int R,int pos,int &cur)
{
    if(!cur) cur=++tree_cnt;
    if(L==R)
    {
        siz[cur]++;
        return;
    }
    int mid=(L+R)>>1;
    if(pos<=mid) modify(L,mid,pos,ls[cur]);
    if(pos>mid) modify(mid+1,R,pos,rs[cur]);
    pushup(cur);
}
int merge(int x,int y)
{
    if(!x||!y) return x+y;
    s1+=siz[ls[x]]*siz[rs[y]];
    s2+=siz[ls[y]]*siz[rs[x]];
    ls[x]=merge(ls[x],ls[y]);
    rs[x]=merge(rs[x],rs[y]);
    pushup(x);
    return x;
}
void dfs(int &x)
{
    int val;
    read(val);
    x=++tree_cnt;
    if(!val) dfs(ls[x]),dfs(rs[x]);
    else vis[x]=true,modify(1,n,val,rt[x]);
}
void work(int x)
{
    if(vis[x]) return;
    work(ls[x]),work(rs[x]),s1=s2=0;
    rt[x]=merge(rt[ls[x]],rt[rs[x]]);
    ans+=min(s1,s2);
}
int main()
{
    read(n);
    dfs(root);
    work(root);
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lhm-/p/12229829.html
今日推荐