UVALive-4329 Ping pong (树状数组)
题目大意:有n个数排成一列,问从中能找出几个三元组(ai,aj,ak)满足i<j<k并且这三个数严格单调。
题目分析:枚举中间的数字aj,如果aj前面有c(j)个数a(j)小,后面有d(j)个数比a(j)小,那么aj为中间数时,共有c(j)*(n-j-d(j))+d(j)*(j-1-c(j))。定义x(i)表示 i 出现过几次,当枚举到aj 时,只需统计sum(x(1)~x(a(j)-1)便得c(j),d(j)求法类似。
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<vector> # include<queue> # include<list> # include<set> # include<map> # include<string> # include<cmath> # include<cstdlib> # include<algorithm> using namespace std; # define LL long long const int N=1005; const int INF=1000000000; const LL oo=0x7fffffffffffffff; const double eps=1e-10; int n; int maxn; int a[N*100]; int b[N*100]; int c[N*100]; int d[N*100]; int lowbit(int x) { return (x&(-x)); } int Query(int x) { int res=0; while(x>0) { res+=b[x]; x-=lowbit(x); } return res; } void Update(int x,int k) { while(x<=maxn) { b[x]+=k; x+=lowbit(x); } } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&n); maxn=0; for(int i=1;i<=n;++i){ scanf("%d",a+i); maxn=max(maxn,a[i]); } memset(b,0,sizeof(b)); for(int i=1;i<=n;++i){ c[i]=Query(a[i]-1); Update(a[i],1); } memset(b,0,sizeof(b)); for(int i=n;i>=1;--i){ d[i]=Query(a[i]-1); Update(a[i],1); } LL ans=0; for(int i=1;i<=n;++i){ ans+=c[i]*(n-i-d[i]); ans+=d[i]*(i-1-c[i]); } printf("%lld\n",ans); } return 0; }