hdu 5147——Sequence II

题意:HDU提供了中文题目。。。

有一个长度为n的数列A,数列中的每个数都不小于1且不大于n,且数列中不存在两个相同的数.
请统计有多少四元组(a,b,c,d)满足:
1. 
  
  
   
   1a<b<c<dn
  
  
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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值