bzoj2212 Tree Rotations 线段树合并+动态开点

题目传送门

思路:

  区间合并线段树的题,第一次写,对于一颗子树,无论这个子树怎么交换,都不会对其他子树的逆序对造成影响,所以就直接算逆序对就好。

  注意叶子节点是1到n的全排列,所以每个权值都只会出现1次,合并很好写。

  注意动态开点,最多n个叶子节点,然后每次查询用到log个子树节点,(这句话似乎有语病)所以要开nlogn的空间。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
#define fpn() freopen("simple.in","r",stdin)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=200005;
int n,q,tot,r,k,cnt;
int R[maxn*30],rt[maxn*30],L[maxn*30],val[maxn*30],ch[maxn*30][2];
ll sum[maxn*30],ans,anl,anr;
void read(int &r){
    r=++tot;
    scanf("%d",&val[r]);
    if(!val[r]){
        read(ch[r][0]);
        read(ch[r][1]);
    }
}
void pushup(int x){
    sum[x]=sum[L[x]]+sum[R[x]];
}
void insert(int &x,int l,int r,int p){
    x=++cnt;
    int mid=(l+r)>>1;
    if(l==r){
        sum[x]=1;
        return;
    }
    if(p<=mid)insert(L[x],l,mid,p);
    else insert(R[x],mid+1,r,p);
    pushup(x);
}
int merge(int x,int y){
    if(!x)return y;
    if(!y)return x;
    anl+=sum[L[x]]*sum[R[y]];
    anr+=sum[L[y]]*sum[R[x]];
    L[x]=merge(L[x],L[y]);
    R[x]=merge(R[x],R[y]);
    pushup(x);
    return x;
}
ll dfs(int x){
    ll ans=0;
    if(!val[x]){

        ans+=dfs(ch[x][0])+dfs(ch[x][1]);
        anl=anr=0;
        rt[x]=merge(rt[ch[x][0]],rt[ch[x][1]]);
        ans+=min(anl,anr);
    }else{
        insert(rt[x],1,n,val[x]);
    }
    return ans;
}
int main(){
    scanf("%d",&n);
    read(r);
    ans=dfs(1);
    cout<<ans<<endl;
}

原文地址:https://www.cnblogs.com/mountaink/p/10421234.html

时间: 2024-10-11 09:02:13

bzoj2212 Tree Rotations 线段树合并+动态开点的相关文章

BZOJ2212 [Poi2011]Tree Rotations 线段树合并 逆序对

原文链接http://www.cnblogs.com/zhouzhendong/p/8079786.html 题目传送门 - BZOJ3286 题意概括 给一棵n(1≤n≤200000个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少. 题解 线段树合并. 博主很懒,题解不写了. 这份代码是仿照别人的写的. 代码 #include <cstring> #include <cstdio> #include <cmath> #include <al

bzoj2212 [Poi2011]Tree Rotations 线段树合并

Description 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

[POI2011]ROT-Tree Rotations 线段树合并|主席树 / 逆序对

题目[POI2011]ROT-Tree Rotations [Description] 现在有一棵二叉树,所有非叶子节点都有两个孩子.在每个叶子节点上有一个权值(有\(n\)个叶子节点,满足这些权值为\(1..n\)的一个排列).可以任意交换每个非叶子节点的左右孩子. 要求进行一系列交换,使得最终所有叶子节点的权值按照中序遍历写出来,逆序对个数最少. [Input Format] 第一行一个整数\(n\). 下面每行,一个数\(x\). 如果\(x =0\),表示这个节点非叶子节点,递归地向下读

BZOJ_2212_[Poi2011]Tree Rotations_线段树合并

Description 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

[线段树合并] Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数

给一棵 N 个点的树,每个点有一个权值,求每个点的子树中有多少个点的权值比它大. 考虑线段树合并,将权值离散化,每个点开一棵权值线段树. 求答案时直接在权值线段树上查询,线段树合并时类似于可并堆. 要注意的是线段树要动态开点,合并时别忘了 up. 内存什么的最好算一下,数组别开小了. 1 #include<bits/stdc++.h> 2 #define rep(i,a,b) for(register int i=a;i<=b;++i) 3 #define rpd(i,a,b) for(

[BZOJ 2212] [Poi2011] Tree Rotations 【线段树合并】

题目链接:BZOJ - 2212 题目分析 子树 x 内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆序对. 左右子树内部的逆序对与是否交换左右子树无关,是否交换左右子树取决于交换后 “跨越 x 左子树与右子树的逆序对” 是否会减小. 因此我们要求出两种情况下的逆序对数,使用线段树合并,对每个节点建一棵线段树,然后合并的同时就求出两种情况下的逆序对. 代码 #include <iostream> #include <cstdli

bzoj 2212 : [Poi2011]Tree Rotations (线段树合并)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2212 思路:用线段树合并求出交换左右儿子之前之后逆序对的数量,如果数量变小则交换. 实现代码: #include<bits/stdc++.h> using namespace std; #define ll long long const int M = 4e5+10; int n,cnt,idx; ll ans,cnt1,cnt2; int v[M],l[M],r[M],root[

神奇的操作——线段树合并(例题: BZOJ2212)

什么是线段树合并? 首先你需要动态开点的线段树.(对每个节点维护左儿子.右儿子.存储的数据,然后要修改某儿子所在的区间中的数据的时候再创建该节点.) 考虑这样一个问题: 你现在有两棵权值线段树(大概是用来维护一个有很多数的可重集合那种线段树,若某节点对应区间是\([l, r]\),则它存储的数据是集合中\(\ge l\).\(\le r\)的数的个数),现在你想把它们俩合并,得到一棵新的线段树.你要怎么做呢? 提供这样一种算法(tree(x, y, z)表示一个左儿子是x.右儿子是y.数据是z的

线段树合并(【POI2011】ROT-Tree Rotations)

线段树合并([POI2011]ROT-Tree Rotations) 题意 现在有一棵二叉树,所有非叶子节点都有两个孩子.在每个叶子节点上有一个权值(有nn个叶子节点,满足这些权值为1-n1-n的一个排列).可以任意交换每个非叶子节点的左右孩子. 要求进行一系列交换,使得最终所有叶子节点的权值按照前序遍历序写出来,逆序对个数最少. 解法 我们对每一个叶子节点建立一颗权值线段树,然后,我们考虑将两个叶子节点上的线段树合并起来,然后我们考虑逆序对的个数. 如果我们将左儿子的线段树放在前面,则产生的逆