杭电hdu 5147 Sequence II (树状数组)

本文介绍了一种针对特定序列的四元组计数算法,利用树状数组高效计算序列中符合条件的四元组数量。文章详细解释了算法原理及其实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Sequence II

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


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



首先分析题干:这里的四元组除了位子有相关的大小限制条件之外呢,我们是没有任何相关的关系的,所以我们这里拆分成两个二元组来做。例如样例:

从3开始,13能够组成一组二元组,3之后能够形成3组二元组。

然后是2,1 2能够组成一组二元组,2之后能够形成1组二元组。

然后相乘并且相加1*3+1*1得到结果4.所以呢,我们这里的思维就很好形成了。找到当前数字之前的数字里边比他小的数字和,就是他之前和他能够组成的二元组的组数。

然后再找到这个数之后能够形成的组数。然后相乘就能得到结果。

但是这里,当前数字之后的组数是如何计算的呢?我们这里拿样例来说明。从后向前计算:

5 之后没有数据,能够形成0组二元组。

4之后5比他大,能够形成1组二元组。这里表示数字2之后能够形成1组二元组。

2之后4 5比他大,2 4 2 5能够形成两组二元组 ,加上 4 5的二元组,这里表示数字3之后能够形成3组二元组。

3之后 4 5比他大 3 4 3 5能够形成两组二元组 ,加上 2 4 2 5 4 5三组,这里表示数字1之后能够形成5组二元组。依次类推即可。

然后我们这里初始化为:

int b[100005];//表示当前数字前边能够组成的二元组数。
int c[100005];//表示当前数字之后能够形成的二元组数。
int a[100005];//元素。

那么我们如何确定大小关系呢?

我们这里应用树状数组来解决。

这里先贴上树状数组相关的三个函数:


int tree[100005];//树
int lowbit(int x)//lowbit
{
    return x&(-x);
}
int sum(int x)//求和求的是比当前数小的数字之和,至于这里如何实现,很简单:int sum=sum(a[i]);
{
    int sum=0;
    while(x>0)
    {
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
void add(int x,int c)//加数据。
{
    while(x<=n)
    {
        tree[x]+=c;
        x+=lowbit(x);
    }
}

这里我们能够通过sum(a[i])来求得有多少个数比a【i】小,能够通过sum(n)-sum(a[i])来求得有多少个数比a【i】 大。然后我们就能很容易求出b【】和c【】:

        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++)
        {
            b[i]=sum(a[i]);//a[i]之前,比a[i]小的数的个数
            add(a[i],1);
        }
        memset(tree,0,sizeof(tree));//注意这里要memset一下。
        for(int i=n;i>=1;i--)
        {
            c[i]=sum(n)-sum(a[i])+c[i+1];//a[i]之后,能形成二元组的组数.
            add(a[i],1);
        }

然后上完整的AC代码:

 

#include<stdio.h>
#include<string.h>
using namespace std;
int tree[100005];
int b[100005];
int c[100005];
int a[100005];
int n;
int lowbit(int x)
{
    return x&(-x);
}
int sum(int x)
{
    int sum=0;
    while(x>0)
    {
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
void add(int x,int c)
{
    while(x<=n)
    {
        tree[x]+=c;
        x+=lowbit(x);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        memset(tree,0,sizeof(tree));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++)
        {
            b[i]=sum(a[i]);//a[i]之前,比a[i]小的数的个数
            add(a[i],1);
        }
        memset(tree,0,sizeof(tree));
        for(int i=n;i>=1;i--)
        {
            c[i]=sum(n)-sum(a[i])+c[i+1];//a[i]之后,能形成二元组的组数.
            add(a[i],1);
        }
        long long output=0;
        for(int i=2;i<=n-2;i++)
        {
            long long x=b[i];
            long long y=c[i+1]; 
            output+=x*y;
        }
        printf("%I64d\n",output);
    }
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值