hdu 5792 离散化+树状数组

本文介绍了一种解决特定组合计数问题的高效算法。针对给定整数序列,利用离散化与树状数组技术预处理数据,有效计算满足特定顺序及大小关系的四元组数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



链接:戳这里


World is Exploding

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Problem Description
Given a sequence A with length n,count how many quadruple (a,b,c,d) satisfies: a≠b≠c≠d,1≤a<b≤n,1≤c<d≤n,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,A2⋯An.
1≤n≤50000
0≤Ai≤1e9
 
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


题意:

给出一个长度为n的整数序列,要求输出有多少个四元组{a,b,c,d}。

满足1<=a<b<=n,1<=c<d<=n,a!=b!=c!=d 且 Va<Vb && Vc>Vd


思路:

预处理出mn[i],mx[i],hmn[i],hmx[i]

mn[i]:[1,i-1]之间有多少个数比ai小

mx[i]:[1,i-1]之间有多少个数比ai大

hmn[i]:[i+1,n]之间有多少个数比ai小

hmx[i]:[i+1,n]之间有多少个数比ai大

sum[i]:表示以当前第i个数结尾,有多少对a,b满足条件。也就是前缀和mn[i]

这些都可以通过离散化+树状数组实现

接下来枚举当前ai作为Vd,那么就存在mx[i]个Vc,以及sum[n]对Va,Vb。ans+=mx[i]*sum[n]

由于答案会多算进去a=c || a=d || b=c || b=d的情况,那么枚举这四种情况减去就可以了(a=c那么必定b!=d,同理其他

a=c:ans-=hmn[i]*hmx[i]

a=d:ans-=mx[i]*hmx[i]

b=c:ans-=mn[i]*hmn[i]

b=d:ans-=mx[i]*mn[i]


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#include<cmath>
#include<bitset>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef long double ld;
#define INF (1ll<<60)-1
#define Max 1e9
using namespace std;
int n,m;
int a[50010],b[50010];
int num[50010];
int mn[50010],mx[50010];   /// 分别表示[1,i-1]有多少个数比ai小  大
int hmn[50010],hmx[50010]; /// 分别表示[i+1,n]有多少个数比ai小  大
int lowbit(int x){
    return x&-x;
}
void Add(int x){
    while(x<=m) {
        num[x]++;
        x+=lowbit(x);
    }
}
int query(int x){
    int ans=0;
    while(x>=1){
        ans+=num[x];
        x-=lowbit(x);
    }
    return ans;
}
ll sum[50010];
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+n+1);
        m=unique(b+1,b+n+1)-(b+1);
        for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+m+1,a[i])-b;
        mst(num,0);
        mst(sum,0);
        for(int i=1;i<=n;i++){
            mn[i]=query(a[i]-1);
            mx[i]=query(m)-query(a[i]);
            sum[i]=sum[i-1]+mn[i]*1LL;
            Add(a[i]);
            //printf("%d %d\n",mn[i],mx[i]);
        }
        mst(num,0);
        for(int i=n;i>=1;i--){
            hmn[i]=query(a[i]-1);
            hmx[i]=query(m)-query(a[i]);
            Add(a[i]);
            //printf("%d %d\n",hmn[i],hmx[i]);
        }
        ll ans=0;
        for(int i=1;i<=n;i++){
            ll num1=1LL*mx[i];
            ll num2=1LL*sum[n];
            ans+=num1*num2;
        }
        /// a=c b!=d
        for(int i=1;i<=n;i++){
            ll num1=hmn[i];
            ll num2=hmx[i];
            ans-=num1*num2;
        }
        /// a=d b!=c
        for(int i=1;i<=n;i++){
            ll num1=mx[i];
            ll num2=hmx[i];
            ans-=num1*num2;
        }
        /// b=c a!=d
        for(int i=1;i<=n;i++){
            ll num1=mn[i];
            ll num2=hmn[i];
            ans-=num1*num2;
        }
        /// b=d a!=c
        for(int i=1;i<=n;i++){
            ll num1=mx[i];
            ll num2=mn[i];
            ans-=num1*num2;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}



评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值