题意:HDU提供了中文题目。。。
有一个长度为n的数列A,数列中的每个数都不小于1且不大于n,且数列中不存在两个相同的数. 请统计有多少四元组(a,b,c,d)满足: 1. 1≤a<b<c<d≤n 2. Aa<Ab 3. Ac<Ad
思路:
题解给出的方法是枚举c,我想的方法是枚举d。题解的思路比我的简单。。。
假设数组保存与A[ ]里面
我的想法是用两个树状数组,have用于维护到目前为止在数字i之前已经出现过的数字有多少个。cnt[i]表示在位置i及i之前有多少个满足要求的a和b。首先cnt[i]一定包含cnt[i-1],多出来的部分就是b=i的情况,所以cnt[i]=cnt[i-1]+{位置i前面比t小的数的个数},其中t为A[I]。计算比t小的数字的方法就是利用have这个树状数组。
对于一个d,需要的就是找到一个c,使得c<d,并且第c个数小于第d个数。对于每个d和c,满足要求的a和b的个数就是在c前面出现的a,b可行数量总和,及sigma(cnt[1...c-1])。
这样,就可以再维护一个树状数组,用来保存cnt的前缀和。这样,对于一个d,满足条件的数量就是sigma(1,A[d]-1)注意这里的第二个数组不能按照位置来编号,应当按照数字来编号。即sumv[m]保存的是数字m之前有多少个满足要求的(a,b)组合,而不是A数组的第m个元素。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=50005;
ll sumv[maxn*3];
ll cnt[50005];
int have[maxn*3];
int n;
int v;
int yl,yr;
inline int lowbit(int x)
{
return x&-x;
}
void sethave(int x)
{
while(x<=n)
{
have[x]++;
x+=lowbit(x);
}
}
int queryhave(int x)
{
int ret=0;
while(x>0)
{
ret+=have[x];
x-=lowbit(x);
}
return ret;
}
inline void update(int x,int d)
{
while(x<=n)
{
sumv[x]+=d;
x+=lowbit(x);
}
}
inline ll query(int x)
{
ll ret=0;
while(x>0)
{
ret+=sumv[x];
x-=lowbit(x);
}
return ret;
}
int main()
{
// freopen("data.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=0;i<=n*2;++i){
have[i]=0;
}
for(int i=0;i<=n;++i)
{
sumv[i]=0;
cnt[i]=0;
}
ll tot=0;
for(int i=1;i<=n;++i)
{
int a;
scanf("%d",&a);
cnt[i]=cnt[i-1];
cnt[i]+=queryhave(a-1);
sethave(a);
if(i>=3)
tot+=query(a-1);
update(a,cnt[i-1]);
}
#ifdef ONLINE_JUDGE
printf("%I64d\n",tot);
#else
cout<<tot<<endl;
#endif // _ONLINE_JUDGE
}
return 0;
}
另外一种have用线段树维护的代码,效率比较低,但是出现了一个问题
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=50005;
ll sumv[maxn*3];
ll cnt[50005];
int have[maxn*3];
int n;
int yl,yr;
void sethave(int o,int l,int r,int a)
{
int m= l + (r-l)/2 ;
if(l==r)
{
have[o]=1;
}
else{
if(a<=m)sethave(o*2,l,m,a);
else sethave(o*2+1,m+1,r,a);
have[o]=have[o*2]+have[o*2+1];
}
}
int queryhave(int o,int l,int r)
{
if(yl<=l&&yr>=r)
{
return have[o];
}
else
{
int m=l+(r-l)/2;
int ret=0;
if(yl<=m)ret+=queryhave(o*2,l,m);
if(yr>m)ret+=queryhave(o*2+1,m+1,r);
return ret;
}
}
inline int lowbit(int x)
{
return x&-x;
}
inline void update(int x,int d)
{
while(x<=n)
{
sumv[x]+=d;
x+=lowbit(x);
}
}
inline ll query(int x)
{
ll ret=0;
while(x>0)
{
ret+=sumv[x];
x-=lowbit(x);
}
return ret;
}
int main()
{
// freopen("data.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
//memset(have,0,sizeof(have));
for(int i=1;i<=min(n*4,maxn*3-1);++i)
{
have[i]=0;
}
for(int i=0;i<=n;++i)
{
sumv[i]=0;
cnt[i]=0;
flag[i]=false;
}
ll tot=0;
for(int i=1;i<=n;++i)
{
int a;
scanf("%d",&a);
cnt[i]=cnt[i-1];
sethave(1,1,n,a);
yl=1;
yr=a-1;
if(yr>=1)
cnt[i]+=queryhave(1,1,n);
if(i>=3)
tot+=query(a-1);
update(a,cnt[i-1]);
}
#ifdef ONLINE_JUDGE
printf("%I64d\n",tot);
#else
cout<<tot<<endl;
#endif // _ONLINE_JUDGE
}
return 0;
}
这样的代码提交上去以后过了,但是有一个问题,在线段树初始化的时候,如果把i设置成2n,就会初始化不完全,3n也会不完全,一直到4n的时候才能通过。
今天终于明白了。。线段树要开到4n