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


    这道题采用权值线段树合并的解法。


    首先讲一下解法中出现的两个概念:权值线段树与线段树合并。

    所谓权值线段树,可以理解为维护的信息反过来的普通线段树,我个人认为值域线段树这个名字其实要准确一些。

    举个例子,我们将序列$1,1,2,3,4,4,4,5,6,6$中的数依次插入,那么插入完成之后的效果图大概是下面这样的:

    (其中红色为节点的值)

    也就是说,每一个节点维护的值是这个区间内的数出现的次数。

    在实现权值线段树时,我们通常会采用动态开点的方式,也就是不创建无关的节点,当然也可以离散化数据,否则必然会空间超限。

    而线段树合并的原理则是基于线段树较为稳定的结构。

    在合并的过程中,我们将两颗线段树对应位置的节点的值合在一起,创建一颗新的线段树。

    过程大致如下:


    这道题让我们求出逆序对个数最小值,并且允许我们随意交换一个节点的两棵子树。

    考虑一个任意的节点,它的子树先序遍历后的逆序对显然只有三种组成:

    1. 左子树中

    2. 右子树中

    3. 跨越左右子树

    对子树的交换显然不会影响第1,2类,因此我们只需要计算出第三类的最小值即可。

    至于计算则没有什么难度。由于我们维护的是值域,因此左儿子必定比右儿子大,那么我们就用左儿子大小乘以右儿子大小即可得出交换前逆序对个数。交换后同理之。

    这里需要注意,我们能够这样计算是因为无论左右儿子怎么交换,影响的都只有当前部分的逆序对个数,而不会影响深度更浅的节点的值。

    另:这道题处理输入十分窒息,可参考楼下写法。


    AC代码如下:

    509ms 57236kb

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 namespace StandardIO {
     6 
     7     template<typename T>inline void read (T &x) {
     8         x=0;T f=1;char c=getchar();
     9         for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
    10         for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
    11         x*=f;
    12     }
    13 
    14     template<typename T>inline void write (T x) {
    15         if (x<0) putchar('-'),x*=-1;
    16         if (x>=10) write(x/10);
    17         putchar(x%10+'0');
    18     }
    19 
    20 }
    21 
    22 using namespace StandardIO;
    23 
    24 namespace Solve {
    25     
    26     const int N=200200;
    27     
    28     int n;
    29     long long ans,ans1,ans2;
    30     int tot_node;
    31     struct node {
    32         int ls,rs;
    33         long long val;
    34     } tree[N*20];
    35     
    36     void update (int l,int r,int v,int &pos) {
    37         if (!pos) pos=++tot_node;
    38         tree[pos].val++;
    39         if (l==r) return;
    40         int mid=(l+r)>>1;
    41         if (v<=mid) update(l,mid,v,tree[pos].ls);
    42         else update(mid+1,r,v,tree[pos].rs);
    43     }
    44     void merge (int &x,int y) {
    45         if (!x||!y) {
    46             x=x+y;return;
    47         }
    48         tree[x].val+=tree[y].val;
    49         ans1+=(long long)tree[tree[x].rs].val*tree[tree[y].ls].val;
    50         ans2+=(long long)tree[tree[x].ls].val*tree[tree[y].rs].val;
    51         merge(tree[x].ls,tree[y].ls);
    52         merge(tree[x].rs,tree[y].rs);
    53     }
    54     void dfs (int &x) {
    55         int tmp,ls,rs;x=0;
    56         read(tmp);
    57         if (!tmp) {
    58             dfs(ls),dfs(rs);
    59             ans1=ans2=0;
    60             x=ls,merge(x,rs);
    61             ans+=min(ans1,ans2);
    62         } else update(1,n,tmp,x);
    63     }
    64 
    65     inline void solve () {
    66         read(n);
    67         int tmp=0;
    68         dfs(tmp);
    69         write(ans);
    70     }
    71 }
    72 
    73 using namespace Solve;
    74 
    75 int main () {
    76 //    freopen(".in","r",stdin);
    77 //    freopen(".out","w",stdout);
    78     solve();
    79 }
  • 相关阅读:
    pika的阻塞式使用
    使用 Nuget安装DLL
    StackExchange.Redis的使用
    MongoDB 学习笔记(9)--- Limit与Skip方法
    MongoDB 学习笔记(8)---$type 操作符
    MongoDB学习笔记(7)--- 条件操作符
    MongoDB学习笔记(6)--find
    阿里巴巴Java开发规范手册
    python获取当前文件路径
    断网环境下利用pip安装Python离线安装包
  • 原文地址:https://www.cnblogs.com/ilverene/p/9841090.html
Copyright © 2020-2023  润新知