(HDU 5792)2016 Multi-University Training Contest 5 World is Exploding (逆序数、顺序数、树状数组)

思路

要求互不相等的a,b,c,d
1. 满足 a<b Aa<Ab ,即一个升序对
2. 满足 c<d Ac>Ad ,即一个降序对
对于一个升序的序列,其逆序对的个数为 每个数右侧小于这个数的个数
若一个数大于右侧的一个数,则右侧的这个数一定小于左侧的一个数,显然,升序对个数等于降序对
答案可以通过枚举 a c来得到。
因为要求四个数两两不同,所以要减去以下四种情况:
1. a c重合,减去 ni=1 右侧大*右侧小
2. a d重合,减去 ni=1 右侧大*左侧大
3. b c重合,减去 ni=1 右侧小*左侧小
4. b d重合,减去 ni=1 左侧大*右侧大
可以用树状数组预处理出每个数右侧大于这个数的个数,右侧小于这个数的个数,左侧大于这个数的个数,左侧小于这个数的个数。

Trick:
1. 数据要做离散化处理
2. 可能有重复的数,在计算右边小或左边小(a、c)时不需要算进去,但是在去重时要予以考虑

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
#define debug(a) printf("a =: %d\n",a);
const int INF=0x3f3f3f3f;
const int maxn=5e4+50;
const int Mod=1000000007;
const double PI=acos(-1);
typedef long long ll;
using namespace std;


int n;
int c[maxn];

int lowbit(int x){
    return x&(-x);
}

void add(int pos,int ad){
    for(;pos<=n;pos+=lowbit(pos))
        c[pos]+=ad;
}

int query(int pos){
    int ret=0;
    for(;pos>0;pos-=lowbit(pos))
        ret+=c[pos];
    return ret;
}

int rg[maxn],rs[maxn],lg[maxn],ls[maxn];

int s[maxn],tmp[maxn];


void init(){
    map<int,int> ma;
    int cnt=1;
    sort(tmp,tmp+n);
    for(int i=0;i<n;i++){
        if (!ma[tmp[i]]){
            ma[tmp[i]]=cnt++;
        }
    }
    for(int i=0;i<n;i++){
        s[i]=ma[s[i]];
    }


    mem(c,0);
    for(int i=n-1;i>=0;i--){
        int t=s[i];
        rs[i]=query(s[i]-1);
        rg[i]=n-i-1-query(s[i]);
        add(t,1);
    //  cout<<i<<" "<<rs[i]<<" "<<rg[i]<<endl;
    }

    mem(c,0);
    for(int i=0;i<n;i++){
        int t=s[i];

        ls[i]=query(s[i]-1);
        lg[i]=i-query(s[i]);
        add(t,1);
    //  cout<<i<<" "<<ls[i]<<" "<<lg[i]<<endl;
    }



}

ll solve(){
    ll inc=0,dec=0;
    ll ret=0;
    for(int i=0;i<n;i++){
        inc+=ls[i];
        dec+=rs[i];
        ret-=rg[i]*lg[i]+ls[i]*rs[i]+rg[i]*rs[i]+lg[i]*ls[i];
    }
    ret+=inc*dec;
    return ret;

}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
    #endif

    while(scanf("%d",&n)!=EOF){
        for(int i=0,t;i<n;i++){
            scanf("%d",&t);
            s[i]=tmp[i]=t;
        }

        init();
        cout<<solve()<<"\n";
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值