HDU 5792 World is Exploding (树状数组逆序对)

World is Exploding


Problem Description
Given a sequence A with length n,count how many quadruple (a,b,c,d) satisfies: abcd,1a<bn,1c<dn,Aa<Ab,Ac>Ad .
 

Input
The input consists of multiple test cases.
Each test case begin with an integer n in a single line.

The next line contains n integers A1,A2An .
1n50000
0Ai1e9
 

Output
For each test case,output a line contains an integer.
 

Sample Input
  
  
4 2 4 1 3 4 1 2 3 4
 

Sample Output
  
  
1 0
 

题意:

给出一个数列,要求你找出有多少个正序对和逆序对的四元组,下标均不相同且严格增减

分析:

首先计算出所有组合情况,观察发现对于每个端点其实只有四种情况不符合,即以当前点为端点的左升左降,左升右降,右升左降,右升右降右组合而成的四元组,那么对于每个端点用树状数组去统计左边和右边的可组成的正序对和逆序对个数即可,注意重复值特殊处理


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>

using namespace std;

#define maxn 50005

vector<int>v;
int num[maxn],vis[maxn];
int pos[maxn],val[maxn];
int e1[maxn],e2[maxn];
int lg[maxn],rg[maxn],ls[maxn],rs[maxn];
int c[2][maxn];
int n;

bool cmp1(int a,int b)
{
    if(val[a]==val[b]) return pos[a]<pos[b];
    return val[a]<val[b];
}

bool cmp2(int a,int b)
{
    if(val[a]==val[b]) return pos[a]<pos[b];
    return val[a]>val[b];
}

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

void update(int kind,int x,int val)
{
    for(;x<=n;x+=lowbit(x)) c[kind][x]+=val;
}

int query(int kind,int x)
{
    if(x==0) return 0;
    int sum=0;
    for(;x>0;x-=lowbit(x)) sum+=c[kind][x];
    return sum;
}


int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(vis,0,sizeof vis);
        v.clear();
        for(int i=1;i<=n;i++)
        {
            pos[i]=i;
            scanf("%d",&val[i]);
            v.push_back(val[i]);
            e1[i]=e2[i]=i;
        }
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        for(int i=1;i<=n;i++)
        {
            val[i]=1+(lower_bound(v.begin(),v.end(),val[i])-v.begin());
        }
        for(int i=1;i<=n;i++)
        {
            num[i]=vis[val[i]];
            vis[val[i]]++;
        }
        sort(e1+1,e1+n+1,cmp1);
        sort(e2+1,e2+n+1,cmp2);
        memset(c,0,sizeof c);
        long long all1=0,all2=0;
        for(int i=1;i<=n;i++)
        {
            int e=e1[i];
            lg[e]=query(0,pos[e]-1)-num[pos[e]];
            update(0,pos[e],1);
            all1+=lg[e];
            e=e2[i];
            rg[e]=query(1,pos[e]-1)-num[pos[e]];
            update(1,pos[e],1);
            all2+=rg[e];
        }
        memset(c,0,sizeof c);
        for(int i=1;i<=n;i++)
        {
            int e=e1[i];
            ls[e]=query(0,n)-query(0,pos[e]);
            update(0,pos[e],1);
            e=e2[i];
            rs[e]=query(1,n)-query(1,pos[e]);
            update(1,pos[e],1);
        }
        long long ans=all1*all2;
        for(int i=1;i<=n;i++)
        {
            ans-=(long long)rs[i]*rg[i]+(long long)lg[i]*ls[i]+(long long)lg[i]*rg[i]+(long long)ls[i]*rs[i];
        }
        printf("%lld\n",ans);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值