题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=12089
题意:按顺序给定一些人和能量值。问按照顺序选人,中间人能量值在两边人能量值的中间,问最多有多少种选法
思路:一次AC有木有!其实是照着书上题解的思路打的。
设c[i]为某a[i]处左边能量值比他小的人数和,d[i]为右边能量值比他大的人数和,然后一个简单公式即可。关键是怎样快速的求c[i]。
以a[i]值建树状数组,其中c[i]从前往后,d[i]从后往前(小创新,和书上不一样虽然换汤不换药),设存在的点x[a[i]] = 1,然后用不断更新数组的方式得到每个点的c[i],d[i]值。本题要求每个人能量值不重复,如果重复的话x[i]多加值就好。
不看知识点根本做不出来好嘛!其实可以这样想:如何求前一个区间和后一个区间的数值和,除了线段树,就是树状数组了!
源码:#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <iostream>
using namespace std;
typedef long long ll;
int const MAXN = 100000+5;
int a[MAXN],c[MAXN],d[MAXN],x[MAXN],sum[MAXN];
int n,mmax,tmax;
ll res;
int gmax(int a,int b){return a>b?a:b;}
void init()
{
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
memset(x,0,sizeof(x));
memset(sum,0,sizeof(sum));
tmax = mmax = res = 0;
}
int lowbit(int l)
{
return l&-l;
}
int les(int aa)
{
int ans = 0;
// printf("At first , aa = %d\n",aa);
while(aa>0){
// printf("aa = %d\n",aa);
ans += sum[aa];
aa-=lowbit(aa);
}
return ans;
}
int big(int aa)
{
return les(mmax) - les(aa-1);
}
void add(int aa)
{
while(aa<=tmax){
sum[aa]+=1;
aa+=lowbit(aa);
}
}
void check()
{
int i;
printf("c = ");
for(i=0; i<n; i++)
printf("%d ",c[i]);
printf("\n d = ");
for(i=0; i<n ;i++)
printf("%d ",d[i]);
printf("\n");
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
init();
scanf("%d",&n);
for(int i=0; i<n; i++){
scanf("%d",&a[i]);
tmax = gmax(tmax,a[i]);
}
for(int i=0; i<n; i++){
mmax = gmax(mmax,a[i]);
x[a[i]] = 1;
add(a[i]);
c[i] = les(a[i]-1);
}
// for(int i=1; i<=mmax; i++)
// printf("%d ",sum[i]);
// printf("\n");
mmax = 0;
memset(x,0,sizeof(x));
memset(sum,0,sizeof(sum));
for(int i=n-1; i>=0; i--){
mmax = gmax(mmax,a[i]);
x[a[i]] = 1;
d[i] = big(a[i]+1);
res += (n-i-1-d[i])*(i-c[i])+c[i]*d[i];
add(a[i]);
}
// check();
printf("%I64d\n",res);
}
return 0;
}