zoukankan      html  css  js  c++  java
  • hdu 5147 Sequence II【树状数组/线段树】

    Sequence II
    Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

    Problem Description
    Long long ago, there is a sequence A with length n. All numbers in this sequence is no smaller than 1 and no bigger than n, and all numbers are different in this sequence.
    Please calculate how many quad (a,b,c,d) satisfy:

    1≤a<b<c<d≤n
    Aa<Ab
    Ac<Ad
    Input
    The first line contains a single integer T, indicating the number of test cases.
    Each test case begins with a line contains an integer n.
    The next line follows n integers A1,A2,…,An.

    [Technical Specification]
    1 <= T <= 100
    1 <= n <= 50000
    1 <= Ai <= n

    Output
    For each case output one line contains a integer,the number of quad.

    Sample Input
    1
    5
    1 3 2 4 5

    Sample Output
    4

    题意:有一个长度为n的数列A,数列中的每个数都在1到n之间,且数列中不存在两个相同的数。
    请统计有多少四元组(a,b,c,d)满足:

    1≤a<b<c<d≤n
    Aa<Ab
    Ac<Ad


    思路:
    这是道树状数组/线段树基础题。我们平时更新线段树或树状数组时都是按输入顺序从1到n更新,而还有某些题是需要你以他给定的数值大小的范围建树,把输入的数放到相对应的位置上(一般是对应位置标记),然后进行相关操作,本题即使如此。
    我们讲一下树状数组的做法,线段树同理,只不过写起来麻烦一点。这道题我们按道理来说是需要每次确定四个数的,但明显会t,所以我们之多枚举一个数(这道题确定b或者确定c都可以,我们这里讲一下确定b的做法)。既然确定了b,我们就得想办法确定出b前面比它小的数和它后面满足Ac<Ad的个数,所以这里我们引入前缀和,后缀和来维护。对于刚开始而言,树状数组的所有1至n的点都是还没有被标记的,每当输入一个数时,比如输入5,我们就将它放在5的那个位置上(即更新树状数组把5的位置标记为1),这样我们去查询5前面有多少个数被标记过了,说明这些数是先被输入的,位置在这个数前面,这样子我们就维护出了一个在b前面比Ab小的数的个数的数组
    那接下来我们要如何确定b后面的那些呢?这里我们可以稍微算一下,对于输入的第i个数a[i],我们已经算出了前面比它小的数pre[i],那么对于i+1位置的数来说,比它大的数就是n-a[i]-(i-1-pre[i]),n-a[i]代表i这个位置后面还有多少个数,i-1-pre[i]代表i前面有多少个数比a[i]大,这样就可以算出i后面有多少个数比a[i]大。把这个后缀和维护出来,枚举b的位置把每个点的可能种类数加起来便可,注意答案可能爆int。

    代码:

     1 #include "stdio.h"
     2 #include "string.h"
     3 const int N=1e5+5;
     4 
     5 int t,n,a[N],c[N];
     6 long long ans,pre[N],last[N];
     7 
     8 int lowbit(int x)
     9 {
    10     return x&(-x);
    11 }
    12 
    13 int update(int x)
    14 {
    15     for(int i=x;i<=n;i+=lowbit(i))
    16         c[i]++;                      ///标记该点
    17 }
    18 
    19 int getsum(int x)
    20 {
    21     int sum=0;
    22     for(int i=x;i>=1;i-=lowbit(i))
    23         sum+=c[i];                  ///算i位置前比a[i]小的数的个数
    24     return sum;
    25 }
    26 
    27 int main()
    28 {
    29     scanf("%d",&t);
    30     while(t--)
    31     {
    32         ans=0;
    33         memset(c,0,sizeof(c));
    34         memset(pre,0,sizeof(pre));
    35         memset(last,0,sizeof(last));
    36         scanf("%d",&n);
    37         for(int i=1;i<=n;i++)
    38         {
    39             scanf("%d",&a[i]);
    40             pre[i]=getsum(a[i]);           ///pre[i]代表比a[i]小的数的个数
    41             update(a[i]);
    42         }
    43         for(int i=n;i>=1;i--)
    44             last[i]=n-a[i]-(i-1-pre[i]);     ///算出i位置后面比a[i]大的数的个数
    45         for(int i=n;i>=1;i--)
    46             last[i]+=last[i+1];             ///累加即使i点后面满足Ac<Ad的这个数
    47         for(int i=1;i<=n;i++)
    48             ans+=(pre[i]*last[i+1]);       ///枚举b点位置累加答案
    49         printf("%lld
    ",ans);
    50     }
    51     return 0;
    52 }
  • 相关阅读:
    大同世界的Java 和.NET 开发
    关于Memcached 你了解多少?
    C#二进制与字符串之间的相互转换
    Nopcommerce主要用到的技术及特点
    小程序之路
    .NET方面的框架的整理和总结
    如何将FastReportOnlineDesign 灵活的应用到C/S B/S 程序当中?
    WebApi 的CRUD 的方法的应用
    关于EF 通用增删改查的封装
    基于Json序列化和反序列化通用的封装
  • 原文地址:https://www.cnblogs.com/Satan666/p/11279976.html
Copyright © 2011-2022 走看看