Hdu 4000

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40924940/article/details/84933697

本题还是比较有趣的

给一个打乱的 1--n 排列 求有多少种组合  x y z 满足

x <y <z && Ax < Az < Ay

那么我们只需要先对与每个 x 来说 我们只需要统计一下他后边的比他大的数字个数为 a

就包括了一种排列方式,那么我们随意从a个数字里边选两个({C_{a}}^{2}),那么很显然 我们再另一个为 y,一个为z 就成立了,但是可能会出现一种情况就是 Az > Ay,所以 要减去那些 Ax<Ay<Az 的情况

我们从 y 的角度看, 如何统计处每一个 Ax <Ay <Az 同时 x< y< z的序列数目就很好处理了

求一下 y 前边比他小的数,(树状数组轻易解决),在统计以下后边比他大的,因为这个数组是一个排列,所以对于 Ai 整个数组里一种只有 N-Ai 个数字比他大,同时比他大又在他前边的数字 = 他前边数字总数目 - 他前边比他小的数字

我们就成功的解决了问题。

又是一道用树桩数组优化计数的问题。。

以下是 AC 代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long int
const int maxn = 1e5+5;
const int mod = 1e8+7;
const int low(int x){ return x&-x; }
int tree[maxn];
int n,d;
void add(int x,int val)
{
    for(int i=x;i<maxn;i+=low(i))
        tree[i] += val;
}
int sum(int x)
{
    int res = 0;
    for(int i=x;i>0;i-=low(i))
        res += tree[i];
    return res;
}
int main()
{
    int n,t,k,ct = 1;
    scanf("%d",&t);
    while(t--)
    {
        memset(tree, 0, sizeof tree);
        scanf("%d",&n);
        ll ans = 0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&k);
            add(k, 1);
            ll mibefore = sum(k - 1);
            ll mxbefore = (i - 1) - mibefore;
            ll mxafter = n - k - mxbefore;
            if(mxafter >= 2)
                ans += mxafter * (mxafter - 1) / 2;
            ans -= mibefore * mxafter;
            ans = (ans + mod) % mod;
        }
        printf("Case #%d: %lld\n",ct++,ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40924940/article/details/84933697
hdu