hdu 5147 Sequence II BestCoder #23 —树状数组

原题地址:

http://acm.hdu.edu.cn/showproblem.php?pid=5147


Sequence II


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


问题描述
很久很久以前,有一个长度为n的数列A,数列中的每个数都不小于1且不大于n,且数列中不存在两个相同的数.
请统计有多少四元组(a,b,c,d)满足:
1. 
   
   
    
    1a<b<c<dn
   
   
2. 
   
   
    
    Aa<Ab
   
   
3. 
   
   
    
    Ac<Ad
   
   
输入说明
第一行输入一个整数T,表示有T组测试数据.
每组测试数据第一行包含一个整数n,第二行包含n个整数.

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

思路:

要统计四元组的数量我们可以通过枚举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).  //本段为官方题解
代码:
#include "stdio.h"
#include "iostream"
#include "string.h"
#include "algorithm"
using namespace std;

int num[500050],c[500050],cr[500050],bl[500050],n,T;
long long ans,sum,ssum[500050];

int lowbit(int x)
{
	return x&(-x);
}

void s(int i)
{
	if(i>0)
	{
		sum+=c[i];
		s(i-lowbit(i));
	}
}

void update(int i,int val)
{
    while(i<=n)
	{
        c[i] += val;
        i += lowbit(i);
    }
}

long long get_sum(int i)
{
	sum=0;
	s(i);
	return sum;
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		ans=0;
		memset(cr,0,sizeof(cr));
		memset(bl,0,sizeof(bl));
		memset(c,0,sizeof(c));
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&num[i]);
		}		
		for(int i=1;i<=n;i++)
		{
			bl[i]=get_sum(num[i]);
			update(num[i],1);
		}
		memset(c,0,sizeof(c));
		for(int i=1;i<=n;i++)
            num[i]=n+1-num[i];
		for(int i=n;i>0;i--)
		{
			cr[i]=get_sum(num[i]);
			update(num[i],1);
		}	
		memset(c,0,sizeof(c));
		for(int i=1;i<=n;i++)
		{
			update(i,cr[i]);
		}
		for(int i=2;i<n-1;i++)
		{
			ans+=(get_sum(n)-get_sum(i))*bl[i];
		}
		printf("%I64d\n",ans);
	}
	return 0;
}


PS:第一次手写树状数组~A了很开心啦~虽然是在比赛结束以后,学到知识就好了~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值