[bzoj3702] 二叉树

  一个节点的儿子是否交换,不会影响到它和兄弟节点间的逆序对数。

  所以每次合并线段树的时候算一下交换与不交换的逆序对数,然后选个较小值就行了。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define ll long long
 7 using namespace std;
 8 const int maxn=400233,mxnode=200233*20;
 9 int lc[mxnode],rc[mxnode],sz[mxnode],tot;
10 int ls[maxn],rs[maxn],size[maxn],mp[maxn],tt,rt[maxn];
11 int i,j,k,n,m,presz,P,id,root;
12 ll num0,num1,ans;
13
14 int ra;char rx;
15 inline int read(){
16     rx=getchar(),ra=0;
17     while(rx<‘0‘||rx>‘9‘)rx=getchar();
18     while(rx>=‘0‘&&rx<=‘9‘)ra*=10,ra+=rx-48,rx=getchar();return ra;
19 }
20
21 int merge(int a,int b){
22     if(!b){presz+=sz[a];return a;}
23     if(!a){num0+=1LL*sz[b]*presz;return b;}
24     sz[a]+=sz[b];
25     rc[a]=merge(rc[a],rc[b]),lc[a]=merge(lc[a],lc[b]);
26     return a;
27 }
28 void build(int &x,int a,int b){
29     x=++tot,sz[x]=1;
30     if(a==b)return;
31     int mid=a+b>>1;
32     if(P<=mid)build(lc[x],a,mid);else build(rc[x],mid+1,b);
33 }
34 void in(int &x){
35     x=++tt,id=read();
36     if(id)mp[x]=id,size[x]=1;else
37         in(ls[x]),in(rs[x]),size[x]=size[ls[x]]+size[rs[x]];
38 }
39 void dfs(int x){
40     if(size[x]==1){P=mp[x],build(rt[x],1,n);return;}
41     dfs(ls[x]),dfs(rs[x]),
42     presz=num0=0,rt[x]=merge(rt[ls[x]],rt[rs[x]]);
43     num1=1LL*size[ls[x]]*size[rs[x]]-num0;
44 //  printf("  %d %lld %lld\n",x,num0,num1);
45     ans+=min(num0,num1);
46 }
47 int main(){
48     n=read();
49     in(root);//printf("cnt: %d\n",cnt);
50     dfs(1);
51     printf("%lld\n",ans);
52     return 0;
53 }

时间: 2024-10-15 21:46:36

[bzoj3702] 二叉树的相关文章

【bzoj3702】二叉树 权值线段树

神奇的解法 对于每个节点,建出权值线段树 每次查询右子树的权值线段树和左子树的权值线段树,左子树中比右子树小的有多少?右子树比左子树小的有多少?(分别对应不交换的逆序对和交换的逆序对) 将左子树和右子树的权值线段树合并 递归进行这个操作 感觉复杂度很不靠谱,于是想证明一下复杂度 最开始权值线段树共O(nlogn)个节点,最后共O(n)个节点 每次合并两棵树的每个节点都要访问一遍,所以每个节点好像是要访问O(dep[i])次? 但是,合并两棵树后,有些重复的节点被合并到了一起 所以好像有些节点又没

C#实现二叉树的遍历

C#实现二叉树的前序.中序.后序遍历. public class BinaryTreeNode     {         int value;         BinaryTreeNode left;         BinaryTreeNode right;         /// <summary>         /// 前序遍历         /// </summary>         /// <param name="tree">&l

【树4】二叉树的遍历

简介 遍历二叉树就是按照某种顺序,将树中的结点都枚举一遍,且每个结点仅仅访问一次.因为树不是线性的结构,遍历不像线性表那样简单,因此他的遍历需要特点的算法来完成. 从某种角度讲,对二叉树的遍历就是将树形结构转换为线性结构的操作. 二叉树的遍历方法主要有如下几种: 先序遍历:先访问root结点,再先序遍历左子树,再先序遍历右子树. 中序遍历:先中序遍历左子树,再访问root结点,再中序遍历右子树. 后序遍历:先后序遍历左子树,再后序遍历右子树,再访问root结点. 层遍历:从上到下,从左到右,一层

按之字形顺序打印二叉树

题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } }; */ class Solution { public: vector<vect

【数据算法】Java实现二叉树存储以及遍历

二叉树在java中我们使用数组的形式保存原数据,这个数组作为二叉树的数据来源,后续对数组中的数据进行节点化操作. 步骤就是原数据:数组 节点化数据:定义 Node节点对象 存储节点对象:通过LinkedList保存Node节点对象 在操作过程中我们需要将当前结点和前一节点.后一节点进行关系绑定 package tree; import java.util.LinkedList; import java.util.List; /** * 功能:把一个数组的值存入二叉树中,然后进行3种方式的遍历 *

二叉树的后序遍历(暴力版) 小白菜oj 1034

给出二叉树的前序遍历和中序遍历,求二叉树的后序遍历-- 作为一个搜索蒟蒻,我真的没有办法很和谐的A掉,但估计过几天就会写有关这个题的和谐的解法--但只是估计-- 下面讲述我的超暴力解法-- 首先,先由前序遍历得到一个父亲节点,然后再由中序遍历得到这个父亲节点的左子树和右子树中的元素(中序遍历中,该点的左边的所有点,都在它的左子树,右边的都在它的右子树,子树中的根节点是在这些节点的前序遍历中排名最靠前的),然后递归建树,之后在递归求后序遍历即可. 但这个方法有两个比较--&¥--&的问题:1

二叉树的序列化和反序列化

http://blog.csdn.net/qq_27703417/article/details/70958692 先序遍历二叉树,如果遇到空节点,就在str的末尾加上"#!","#"表示这个节点为空,节点值不存在,当然你也可以用其他的特殊字符,"!"表示一个值的结束.如果遇到不为空的节点,假设节点值为3,就在str的末尾加上"3!".现在请你实现树的先序序列化. 先序遍历 import java.util.*; //使用递归

【二叉树】 二叉树基础

在计算机科学中,二叉树是每个节点最多有两个子树的树结构.通常子树被称作"左子树"(left subtree)和"右子树"(right subtree).二叉树常被用于实现二叉查找树和二叉堆. 二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有个结点:深度为k的二叉树至多有个结点:对任何一棵二叉树T,如果其终端结点数为,度为2的结点数为,则. 树和二叉树的三个主要差别: 树的结点个数至少为1,而二叉树的

二叉树的深度

输入一棵二叉树,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度. 思路:使用递归的方法分别计算左右子树的深度 public class Solution { public int TreeDepth(TreeNode pRoot){ return pRoot == null? 0 : Math.max(TreeDepth(pRoot.left),TreeDepth(pRoot.right)) + 1;     } }