HDU5792(树状数组)

PS:思维还是太弱了,计算方法太神奇!

题意:就是找四个不同的数,a<b,c>d.算种数

分析:简化一下问题,如果单纯求a<b和c>d,树状数组求逆序对即可。但是这里a,b,c,d不能相同。那么就考虑一下怎么去重。枚举一个数,他左边比他小的数就可以当做a,相当于他是b。他左边比他大的数就是c,相当于他是d。同理,看右边是一样的。那样把左边所有小于当前数的数求和,大于的一样。乘积是什么。就是算可以重复的总种数。这时候的重点就是考虑去重。首先就按我上面说的那个例子,就相当于重复计算了b==d,这时候要去掉它。这时候就要去掉它的左小和左大的乘积,因为有a<b,c>d,所以a!=c,不会多减。同理去掉其他三种情况即可。见代码详解。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
int a[50005],b[50005];
int bit[50005];
int n,m;
int query(int i)
{
    int ans=0;
    while(i)
    {
        ans+=bit[i];
        i-=(i&-i);
    }
    return ans;
}

void add(int i)
{
    while(i<=m)
    {
        bit[i]++;
        i+=i&(-i);
    }
}
int lmin[50005],lmax[50005],rmin[50005],rmax[50005];
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
        sort(b+1,b+1+n);m=unique(b+1,b+1+n)-(b+1);
        for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+m+1,a[i])-b;//离散化,可能有重复。
        memset(bit,0,sizeof(bit));
        for(int i=1;i<=n;i++)
        {
            lmin[i]=query(a[i]-1);
            lmax[i]=query(m)-query(a[i]);
            //printf("%d %d\n",lmin[i],lmax[i]);
            add(a[i]);
        }
        memset(bit,0,sizeof(bit));
        for(int i=n;i>0;i--)
        {
            rmin[i]=query(a[i]-1);
            rmax[i]=query(m)-query(a[i]);
            add(a[i]);
        }
        ll ans=0,x=0,y=0;
        for(int i=1;i<=n;i++)//总的
        {
            x+=rmin[i];
            y+=rmax[i];
        }
        ans=x*y;
        //b==d
        for(int i=1;i<=n;i++)
        {
            x=lmin[i];
            y=rmin[i];
            ans-=x*y;
        }
        //a==d
        for(int i=1;i<=n;i++)
        {
            x=lmax[i];
            y=rmax[i];
            ans-=x*y;
        }
        //b==c
        for(int i=1;i<=n;i++)
        {
            x=lmin[i];
            y=lmax[i];
            ans-=x*y;
        }
        //a==c
        for(int i=1;i<=n;i++)
        {
            x=rmin[i];
            y=rmax[i];
            ans-=x*y;
        }
        printf("%lld\n",ans);
    }
    return 0;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值