HDU 5147 Sequence II 树状数组

点击打开链接


Sequence II

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 577    Accepted Submission(s): 226


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.  1a<b<c<dn
2.  Aa<Ab
3.  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
 

Source
 
官方题解:
要统计四元组的数量我们可以通过枚举c,然后统计区间[1,c-1]有多少二元组(a,b)满足a
     
     
      
      <
     
     b且
     
     
      
      Aa<Ab
     
     ,以及统计出区间[c+1,n]有多少d满足
     
     
      
      Ac<Ad
     
     ,根据乘法原理,把这两项乘起来就可以统计到答案里了.
然后我们来处理子问题:区间[1,c-1]内有多少二元组(a,b).那么我们可以枚举b,然后统计区间[1,b-1]内有多少a满足
     
     
      
      Aa<Ab
     
     ,那么这个可以通过用树状数组询问前缀和来实现.
时间复杂度是O(nlogn).
//1684MS	1840K
#include<stdio.h>
#include<string.h>
#define ll __int64
#define N 50007
int a[N],pre[N],suf[N],C[N];//pre记录前缀,suf记录后缀
int lowbit(int x)
{
    return x&(-x);
}
void add(int pos,int val)
{
    while(pos<N)
    {
        C[pos]+=val;
        pos+=lowbit(pos);
    }
}
int getsum(int x)
{
    int result=0;
    while(x>0)
    {
        result+=C[x];
        x-=lowbit(x);
    }
    return result;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        memset(C,0,sizeof(C));
        for(int i=1;i<=n;i++)//计算前缀的个数
        {
            pre[i]=getsum(a[i]);
            add(a[i],1);
        }
        memset(C,0,sizeof(C));
        for(int i=n;i>=1;i--)//计算后缀的个数
        {
            suf[i]=n-i-getsum(a[i]);//倒着输入,第i个数后面有n-i个数,再看看这n-i个数中是不是存在比a[i]小的
            add(a[i],1);
        }
        ll ans=0,p=0;//p代表前缀的总个数
        for(int i=1;i<n;i++)//枚举c的位置
        {
            ans+=suf[i]*p;
            p+=pre[i];
        }
        printf("%I64d\n",ans);
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值