题目大意:
给出一个元素个数为n的序列num[],元素值两两不同,问序列中满足 num[i]<num[j]<num[k] 或 num[i]>num[j]>num[k] (i<j<k) 的三元子序列的个数是多少。
思路:
构想一个虚拟的hash数组,各项值默认为0,若元素num[i]存在,则hash[num[i]]=1。
如此以来,若想知道比num[i]小的元素的个数,只需要求hash[1…num[i]-1]的和即可。而这个和可以由树状数组c[]来维护。因此hash数组并不需要实际定义。
任意时刻,执行树状数组的sum(num[i]-1)操作,即求取hash[1…num[i]-1]的和,便可知当前比num[i]小的元素的数量。
定义a[i]为从元素1到元素i-1之间,小于元素i的元素的数量,b[i]为从元素i+1到元素n之间,小于元素i的元素的数量。那么a[j]*(n-j-b[j])便是所有以元素j为中间元素的三元升序序列的数量。求取降序三元序列的方法同理。
代码:
#include<iostream> #include<stdio.h> using namespace std; #define datasize 100000 //datasize即数据范围,在这里并不是元素数量,而是元素值域。 int a[20000+1],b[20000+1],c[datasize+1],num[datasize+1]; //num[]用于存储元素。 //a[i]表示从元素1到元素i-1之间,小于元素i的元素的数量。 //b[i]表示从元素i+1到元素n之间,小于元素i的元素的数量。 //c[]为树状数组。 int lowbit(int x) { return x&-x; } int sum(int end) //求取虚拟hash数组1至end的和。 { int s=0; while(end>0) { s+=c[end]; end-=lowbit(end); } return s; } void modify(int pos) //在虚拟hash数组的第pos位上+1。 { while(pos<=datasize) { c[pos]++; pos+=lowbit(pos); } } int main() { int t; scanf("%d",&t); for(int i=1;i<=t;i++) { int n; scanf("%d",&n); for(int j=1;j<=n;j++) scanf("%d",&num[j]); memset(c,0,sizeof(c)); for(int j=1;j<=n;j++) { a[j]=sum(num[j]-1); //为a[]赋值 modify(num[j]); } memset(c,0,sizeof(c)); for(int j=n;j>=1;j--) { b[j]=sum(num[j]-1); //为b[]赋值 modify(num[j]); } long long res=0; for(int j=1;j<=n;j++) res+=a[j]*(n-j-b[j])+(j-1-a[j])*b[j]; //n-j表示元素j+1到n之间元素的数量,n-j-b[j]即是元素j+1到n之间大于元素j的元素数量。 //a[j]*(n-j-b[j])便是所有以元素j为中间元素的三元升序序列的数量。 //(j-1-a[j])*b[j]同理。 printf("%I64d\n",res); } }