• Codeforces Round #261 (Div. 2) D. Pashmak and Parmida's problem (树状数组求逆序数 变形)


    题目链接

    题意:给出数组A,定义f(l,r,x)为A[]的下标l到r之间,等于x的元素数。i和j符合f(1,i,a[i])>f(j,n,a[j]),求i和j的种类数。

    我们可以用map预处理出 f(1, i, a[i]) 和 f(j, n, a[j]) ,记为s1[n], s2[n]。

    这样就变成求满足 s1[i] > s[j], i < j 情况的数量了,你会发现跟求逆序对一样

    分析:

    做题的时候想到过逆序数,但是很快放弃了,还是理解不深刻吧,,,233.

    看了这个博客以后才明白这些过程:http://www.cnblogs.com/yuiffy/p/3916512.html

    使用树状数组统计小于某数的元素数量。

    我们可以先把f(1,i,a[i])和f(j,n,a[j])写出来,观察一下,例如样例1:

    n=7

    A  1  2  1  1  2  2  1

    R  4  3  3  2  2  1  1

    L  1  1  2  3  2  3  4

    其中A为给定的数组,Rj为f(j,n,a[j]),Li为f(1,i,a[i])。

    对每个Li,我们要统计的其实就是符合(j>i,且Rj<Li)的Rj的个数。就是这个Li右边有多少个比它小的Rj。

    这样我们可以用树状数组,把Rj各个数的数量全存到树状数组里,例如这个样例就是4有1个,3有2个,2有2个,1有2个。然后从左到右遍历Li,每次从树状数组里删掉Rj,并且求sum(Li-1),也就是树状数组中1~Li-1的和,也就是比Li小的元素个数。

    例如这个样例,到L3时,树状数组里记了2个1和2个2(1个4和2个3在之前被删掉了),L3=2,则sum(L3-1)=sum(2)=1的数量+2的数量=3。ans+=3。

    其实从左到右加一遍以后,树状数组是这个样子的:

    下标 1  2  3  4

    值    2  2  2  1

    不能不说这个过程还是挺难想的

    时间复杂度是O(n*log(n));

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cstdlib>
     5 #include <cmath>
     6 #include <map>
     7 #include <algorithm>
     8 #define LL __int64
     9 const int maxn = 1000000+10;
    10 using namespace std;
    11 int c[maxn], a[maxn], n;
    12 
    13 int lowbit(int x)
    14 {
    15     return x&(-x);
    16 }
    17 void add(int x,int d)
    18 {
    19     while(x <= n)
    20     {
    21         c[x] += d;
    22         x +=lowbit(x);
    23     }
    24 }
    25 LL sum(int x)
    26 {
    27     LL ret = 0;
    28     while(x > 0)
    29     {
    30         ret += c[x];
    31         x -= lowbit(x);
    32     }
    33     return ret;
    34 }
    35 void slove()
    36 {
    37     map<int, int>x, y;
    38     LL ans = 0;
    39     int i;
    40     memset(c, 0, sizeof(c));
    41 
    42     for(i = 1; i <= n; i++)
    43     {
    44         scanf("%d", &a[i]);
    45         x[a[i]] ++;
    46         add(x[a[i]], 1);  //其实树状数组里的下标代表个数,数组里的数代表扫过去的依次产生的该个数的数量。
    47     }
    48     for(i = 1; i <= n; i++)
    49     {
    50         y[a[i]] ++; //要查找的数量++
    51         add(x[a[i]], -1);  //依次把原来的-1
    52         x[a[i]] --;  //x[]里也要减去
    53         ans += sum(y[a[i]]-1); //求从1到 比当前的个数少一的个数的和。
    54     }
    55     printf("%I64d
    ", ans);
    56 }
    57 
    58 int main()
    59 {
    60     while(~scanf("%d", &n))
    61     {
    62         slove();
    63     }
    64     return 0;
    65 }
  • 相关阅读:
    服务器RAID5阵列掉了两块盘恢复数据过程
    sql server数据库错误数据恢复过程
    raid崩溃导致存储虚拟化平台数据丢失
    详解MBR分区结构以及GPT分区结构
    存储硬件故障如何恢复数据库
    服务器raid常见故障解决方案
    误删除VMware虚拟机vmdk文件的恢复方法
    HP P2000 服务器数据恢复+服务器数据恢复通用办法
    DELL EqualLogic PS6100存储数据丢失的解决方案
    IBM DS4800服务器RAID信息丢失数据恢复方法
  • 原文地址:https://www.cnblogs.com/bfshm/p/3916799.html
Copyright © 2020-2023  润新知