zoukankan      html  css  js  c++  java
  • bzoj3295: [Cqoi2011]动态逆序对(树套树)

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 #include <algorithm>
     6 #define maxn 100005
     7 #define maxk 6000005
     8 using namespace std;
     9 
    10 int n,m,size,num[maxn],pos[maxn],tsum[maxn],t1[maxn],t2[maxn];
    11 typedef long long ll;
    12 ll ans;
    13 int sum[maxk],lc[maxk],rc[maxk],root[maxn];
    14 
    15 int lowbit(int x){
    16     return x&(-x);
    17 }
    18 
    19 void tree_insert(int x){
    20     for (int i=x;i<=n;i+=lowbit(i)) tsum[i]++;
    21 }
    22 
    23 int tree_sum(int x){
    24     int temp=0;
    25     for (int i=x;i>0;i-=lowbit(i)) temp+=tsum[i];
    26     return temp;
    27 }
    28 
    29 int query_sum(int k,int l,int r,int x,int y){
    30     if (!k) return 0;
    31     if (l>=x&&r<=y){
    32         return sum[k];
    33     }
    34     int mid=(l+r)/2,temp=0;
    35     if (x<=mid) temp+=query_sum(lc[k],l,mid,x,y);
    36     if (y>mid) temp+=query_sum(rc[k],mid+1,r,x,y);
    37     return temp;
    38 }
    39 
    40 int query(int lim,int l,int r){
    41     int temp=0;
    42     for (int i=lim;i>0;i-=lowbit(i)){
    43         temp+=query_sum(root[i],1,n,l,r);
    44     }
    45     return temp;
    46 }
    47 
    48 void update(int &k,int l,int r,int x){
    49     if (!k) k=++size;
    50     sum[k]++;
    51     if (l==r) return;
    52     int mid=(l+r)/2;
    53     if (x<=mid) update(lc[k],l,mid,x);
    54     else update(rc[k],mid+1,r,x);
    55 }
    56 
    57 void insert(int lim,int x){
    58     for (int i=lim;i<=n;i+=lowbit(i)){
    59         update(root[i],1,n,x);
    60     }
    61 }
    62 
    63 int main(){
    64 //    freopen("dtnxd.in","r",stdin);
    65 //    freopen("dtnxd.out","w",stdout);
    66     scanf("%d%d",&n,&m),ans=size=0;
    67     memset(sum,0,sizeof(sum));
    68     memset(root,0,sizeof(root));
    69     memset(tsum,0,sizeof(tsum));
    70     for (int i=1;i<=n;i++){
    71         scanf("%d",&num[i]),pos[num[i]]=i;
    72         t1[i]=tree_sum(n)-tree_sum(num[i]);
    73         ans+=t1[i];
    74         tree_insert(num[i]);
    75     }
    76     memset(tsum,0,sizeof(tsum));
    77     for (int i=n;i>=1;i--){
    78         t2[i]=tree_sum(num[i]-1);
    79         tree_insert(num[i]);
    80     }
    81     memset(tsum,0,sizeof(tsum));
    82 //    for (int i=1;i<=n;i++) printf("%d %d
    ",t1[i],t2[i]);
    83     int u,v;
    84     for (int i=1;i<=m;i++){
    85         scanf("%d",&u),v=pos[u];
    86         printf("%lld
    ",ans);
    87         ans-=(t1[v]+t2[v]);
    88         ans+=query(v-1,u+1,n);
    89         ans+=query(n,1,u-1);
    90         ans-=query(v,1,u-1);
    91         insert(v,u);
    92     }
    93     return 0;
    94 }
    View Code

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3295

    题目大意:对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

    做法:一种暴力写法,就是每次删除前,重新用树状数组维护逆序对,复杂度mnlogn,是接受不了的。我们考虑每次删除一个数后对ans的影响,其影响就是ans-=前面未被删除的数中比它大的个数+后面未被删除的数中比它小的数的个数。这是一个经典的问题:询问一段区间l~r中权值在x~y范围内的个数,支持修改操作,我们能想到的便是树状数组套动态开点的权值线段树(可持久化线段树),但是恶心的是这题卡空间,我们可以这样优化一下:

    我们用一维树状数组即可预处理出两个数组,t1[],t2[],分别表示初始序列中在i之前的比它大的个数、初始序列中在i之后的比它小的个数,我们就不需要把初始的n个数也加进树套树中了,我们删除一个数,ans-=(t1+t2-在它之前的已被删除的比它大的个数-在它之后已被删除的比它小的个数),我们便只需要将m个数加入到树套树中了,空间复杂度优化了不少,mlogn^2,不会MLE。

    还有一种cdq分治的写法,用的归并排序的思想,以后再写吧。

    树套树。

  • 相关阅读:
    Java实现批量下载《神秘的程序员》漫画
    mysql远程连接:ERROR 1130 (HY000): Host '*.*.*.*' is not allowed to connect to this MySQL server解决办法
    opencv学习_15 (利用cmake查看opencv的源码)
    jobs 命令
    中断子系统6_中断嵌套处理
    JPA一对多映射
    JPA Map映射
    JPA集合映射
    JPA删除实体
    JPA查找实体
  • 原文地址:https://www.cnblogs.com/OYzx/p/5530337.html
Copyright © 2011-2022 走看看