1. 程式人生 > >洛谷P3521 [POI2011]ROT-Tree Rotation [線段樹合並]

洛谷P3521 [POI2011]ROT-Tree Rotation [線段樹合並]

right its inline led open lse bits rotation any

  題目傳送門

Tree Rotation

題目描述

Byteasar the gardener is growing a rare tree called Rotatus Informatikus.

It has some interesting features:

The tree consists of straight branches, bifurcations and leaves.

The trunk stemming from the ground is also a branch.

Each branch ends with either a bifurcation or a leaf on its top end.

Exactly two branches fork out from a bifurcation at the end of a branch - the left branch and the right branch.

Each leaf of the tree is labelled with an integer from the range 1..n1..n.

The labels of leaves are unique.

With some gardening work, a so called rotation can be performed on any bifurcation, swapping the left and right branches that fork out of it.

The corona of the tree is the sequence of integers obtained by reading the leaves‘ labels from left to right.

Byteasar is from the old town of Byteburg and, like all true Byteburgers, praises neatness and order.

He wonders how neat can his tree become thanks to appropriate rotations.

The neatness of a tree is measured by the number of inversions in its corona, i.e. the number of pairs (i,j)(i,j), 1\le i<j\le n1i<jn such that a_i>a_jai?>aj? in the corona a_1,a_2,\cdots,a_na1?,a2?,?,an?.

The original tree (on the left) with corona 3,1,23,1,2 has two inversions.

A single rotation gives a tree (on the right) with corona 1,3,21,3,2, which has only one inversion.

Each of these two trees has 5 branches.

Write a program that determines the minimum number of inversions in the corona of Byteasar‘s tree that can be obtained by rotations.

給一棵n(1≤n≤200000個葉子的二叉樹,可以交換每個點的左右子樹,要求前序遍歷葉子的逆序對最少。

輸入輸出格式

輸入格式:

In the first line of the standard input there is a single integer nn(2\le n\le 200\ 0002n200 000) that denotes the number of leaves in Byteasar‘s tree.

Next, the description of the tree follows.

The tree is defined recursively:

  • if there is a leaf labelled with pp (1\le p\le n1pn) at the end of the trunk (i.e., the branch from which the tree stems),then the tree‘s description consists of a single line containing a single integer pp

  • if there is a bifurcation at the end of the trunk, then the tree‘s description consists of three parts:

    • the first line holds a single number 00
    • then the description of the left subtree follows (as if the left branch forking out of the bifurcation was its trunk),
    • and finally the description of the right subtree follows (as if the right branch forking out of the bifurcation was its trunk).

In tests worth at least 30% of the points it additionally holds that n\le 5\ 000n5 000.

輸出格式:

In the first and only line of the standard output a single integer is to be printed:

the minimum number of inversions in the corona of the input tree that can be obtained by a sequence of rotations.

輸入輸出樣例

輸入樣例#1:
3
0
0
3
1
2
輸出樣例#1:
1

說明

給一棵n(1≤n≤200000個葉子的二叉樹,可以交換每個點的左右子樹,要求前序遍歷葉子的逆序對最少。


  分析:

  線段樹合並的模板題。

  線段樹合並是一種常用的技巧,一般是對權值線段樹合並,方法也很簡單,將兩棵線段樹上的權值相加即可,一般用於權值線段樹。

  另外關於這題的答案統計,在合並的過程中只要把子節點的線段樹放在左邊,當前節點的線段樹放在右邊,然後子節點的左邊與當前節點的右邊相乘得到的就是不交換子節點得到的答案,子節點的右邊與當前節點的左邊相乘得到的就是交換子節點得到的答案。(正確性易證)

  Code:

//It is made by HolseLee on 15th Oct 2018
//Luogu.org P3521
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e7+5;
int n,tot,ls[N],rs[N],seg[N];
ll ans,ret1,ret2;

inline int read()
{
    char ch=getchar(); int num=0; bool flag=false;
    while( ch<0 || ch>9 ) {
        if( ch==- ) flag=false; ch=getchar();
    }
    while( ch>=0 && ch<=9 ) {
        num=num*10+ch-0; ch=getchar();
    }
    return flag ? -num : num;
}

int build(int l,int r,int x)
{    
    seg[++tot]=1;
    if( l==r ) return tot;
    int mid=(l+r)>>1;
    int rt=tot;
    if( x<=mid ) ls[rt]=build(l,mid,x); 
    else rs[rt]=build(mid+1,r,x);
    return rt;
}

int merge(int l,int r,int x,int y)
{
    if( !x || !y ) return x+y;
    if( l==r ) {
        seg[++tot]=seg[x]+seg[y];
        return tot;
    }
    int rt=++tot, mid=(l+r)>>1;
    ret1+=(ll)seg[ls[x]]*seg[rs[y]];
    ret2+=(ll)seg[rs[x]]*seg[ls[y]];
    ls[rt]=merge(l,mid,ls[x],ls[y]);
    rs[rt]=merge(mid+1,r,rs[x],rs[y]);
    seg[rt]=seg[ls[rt]]+seg[rs[rt]];
    return rt;
}

int get()
{
    int tmp=read();
    if( tmp ) return build(1,n,tmp);
    int now=merge(1,n,get(),get());
    ans+=min(ret1,ret2);
    ret1=ret2=0;
    return now;
}

int main()
{
    n=read();
    get(); printf("%lld\n",ans);
    return 0;
}

洛谷P3521 [POI2011]ROT-Tree Rotation [線段樹合並]