HDU 5147 Sequence II (线段树求前缀和、后缀和)

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. 1\leq a < b < c < d \leq n1a<b<c<dn
  2. A_{a} < A_{b}Aa<Ab
  3. A_{c} < A_{d}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 A_{1},A_{2}, \ldots, A_{n}A1,A2,,An.

[Technical Specification] 1 <= T <= 100 1 <= n <= 50000 1 <= A_{i}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. 1\leq a < b < c < d \leq n1a<b<c<dn
2. A_{a} < A_{b}Aa<Ab
3. A_{c} < A_{d}Ac<Ad
输入说明
第一行输入一个整数T,表示有T组测试数据.
每组测试数据第一行包含一个整数n,第二行包含n个整数.

数据范围
1 <= T <= 100
1 <= n <= 50000
1 <= A_{i}Ai <= n
输出说明
对于每组测试数据,输出一个整数表示四元组的数量.
输入样例
1
5
1 3 2 4 5
输出样例
4

解题思路要统计四元组的数量我们可以通过枚举c,然后统计区间[1,c-1]有多少二元组(a,b)满足a<<b且A_a < A_bAa<Ab,以及统计出区间[c+1,n]有多少d满足A_{c} < A_{d}Ac<Ad,根据乘法原理,把这两项乘起来就可以统计到答案里了. 然后我们来处理子问题:区间[1,c-1]内有多少二元组(a,b).那么我们可以枚举b,然后统计区间[1,b-1]内有多少a满足A_{a} < A_{b}Aa<Ab,那么这个可以通过用树状数组询问前缀和来实现. 时间复杂度是O(nlogn)。


代码如下:

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#include <limits.h>
#include <ctime>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-6)
#define inf (1<<28)
#define sqr(x) (x) * (x)
#define mod 1000000007
using namespace std;
typedef long long ll;
typedef unsigned long long ULL;
#define MAX 50005
ll n,a[MAX],f1[MAX],f2[MAX],c[MAX];
//f1[i]第i个数之前有多少个比它小的数,f2[i]第i个数之后有多少个比它大的数
ll lowbit(ll x)
{
    return x&(-x);
}
void update(ll i,ll x)//在第i个位置上增加x
{
    while(i<=n)
    {
        c[i]+=x;
        i+=lowbit(i);
    }
}
ll sum(ll i) //c[1-i]之间的和
{
    ll s=0;
    while(i>0)
    {
        s+=c[i];
        i-=lowbit(i);
    }
    return s;
}
int main()
{
    ll i,t;
    scanf("%I64d",&t);
    while(t--)
    {
        scanf("%I64d",&n);
        for(i=1;i<=n;i++)
            scanf("%I64d",&a[i]);
        memset(c,0,sizeof(c));
        for(i=1;i<=n;i++)
        {
            f1[i]=sum(a[i]);//统计a[i]之前有多少个比它小的数
            update(a[i],1);//在a[i]位置上加1,表示已经存在
        }
        memset(c,0,sizeof(c));
        for(i=n;i>=1;i--)
        {
            f2[i]=(n-i)-sum(a[i]);//倒着输入,第i个数后面有n-i个数,再看看这n-i个数中是不是存在比a[i]小的(即sum(a[i])),减去它们,就是a[i]后面所有比它大的
            update(a[i],1);
        }
        ll ans=0,cnt=0;
        for(i=1;i<=n-1;i++)//枚举c的位置
        {
            ans+=cnt*f2[i];//cnt表示输入顺序中第i-1个数之前有多少个数比第i-1个数小,在本题中也就是比b小的个数,i是c
            cnt+=f1[i];
        }
        printf("%I64d\n",ans);
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值