hdu 4000 题解(树状数组)

本文解析了一个关于寻找特定三元组的问题,利用权值树状数组进行高效计算,并提供了一种新颖的方法来解决更复杂的条件。通过枚举最小元素并结合组合数学技巧,有效地避免了重复计算。

(最近突然无聊,决定给大家秀一下我的图片库)
3.jpg
原题链接:
hdu

题意简述

求一个序列(保证是 [ 1 , n ] [1,n] [1,n]的一个排列, n &lt; = 1 e 5 n&lt;=1e5 n<=1e5)中有多少个三元组 ( i , j , k ) (i,j,k) (i,j,k)满足 i &lt; j &lt; k i&lt;j&lt;k i<j<k ( a [ i ] &lt; a [ k ] &lt; a [ j ] ) (a[i]&lt;a[k]&lt;a[j]) (a[i]<a[k]<a[j])

数据

输入

多组(确定组数)。每组:

n
a1 a2 ... an//读入序列
输出
Case #1:ans
Case #2:ans
...
Case #t:ans//每组输出答案,膜1e9+7
样例

输入
2
6
1 3 2 6 5 4
5
3 5 2 4 1
输出
Case #1: 10
Case #2: 1

思路

(请先保证您会用权值树状数组求出三元上升子序列,也就是把这个题的条件改为 a [ i ] &lt; a [ j ] &lt; a [ k ] a[i]&lt;a[j]&lt;a[k] a[i]<a[j]<a[k]这样的问题,然后看下去)

我们发现 i i i不仅是下标最小,也是值最小,考虑枚举这个 i i i

然后就是要在 i i i后面找有多少 j , k j,k j,k满足 j &lt; k j&lt;k j<k a [ j ] &gt; a [ k ] a[j]&gt;a[k] a[j]>a[k] a [ j ] a[j] a[j] a [ k ] a[k] a[k] &gt; a [ i ] &gt;a[i] >a[i]
.假设我们此时不考虑 a [ j ] &gt; a [ k ] a[j]&gt;a[k] a[j]>a[k],那就好做很多了。我们在 [ i + 1 , n ] [i+1,n] [i+1,n]中比 a [ i ] a[i] a[i]大的数里面随便选两个 ( j , k ) (j,k) (j,k)满足 j &lt; k j&lt;k j<k,且 a [ j ] , a [ k ] &gt; a [ i ] a[j],a[k]&gt;a[i] a[j],a[k]>a[i]
[ i + 1 , n ] [i+1,n] [i+1,n]中有 R R R个比 i i i大,那么此时的答案就是 C R 2 = ( R ∗ ( R − 1 ) / 2 ) C_R^2=(R*(R-1)/2) CR2=(R(R1)/2)

珂是这样多算了哪些答案呢?就是不满足 a [ j ] &gt; a [ k ] a[j]&gt;a[k] a[j]>a[k]的答案(理论上 a a a不大于 b b b a &lt; = b a&lt;=b a<=b,但是排列里面不会取等,所以这里不满足 a [ j ] &gt; a [ k ] a[j]&gt;a[k] a[j]>a[k]就是 a [ j ] &lt; a [ k ] a[j]&lt;a[k] a[j]<a[k])。
然后这样子的答案,我们现在是没法求的,但是珂以在以后去除掉。而现在要考虑的,是把以前的去除掉。就是要在找一个 i ′ i&#x27; i,在右边找一个 j j j,使得 a [ i ] &gt; a [ i ′ ] a[i]&gt;a[i&#x27;] a[i]>a[i], a [ j ] &gt; a [ i ′ ] a[j]&gt;a[i&#x27;] a[j]>a[i],但是 a [ i ] &lt; a [ j ] a[i]&lt;a[j] a[i]<a[j],把这样的情况给去掉。这个自然是很好找的,就是"三元上升子序列"的做法了。
设左边有 L L L个比 a [ i ] a[i] a[i]小,右边有 R R R个比 a [ i ] a[i] a[i]大,情况数就是 L ∗ R L*R LR

(最后说一下,其实我们珂以由“ 左 边 &lt; a [ i ] 左边&lt;a[i] <a[i]”的情况数推算出“ 右 边 &gt; a [ i ] 右边&gt;a[i] >a[i]”的情况数的。
设“ 左 边 &lt; a [ i ] 左边&lt;a[i] <a[i]”有 L L L个。
那么
左 边 &gt; a [ i ] 左边&gt;a[i] >a[i] ( i − 1 ) − L (i-1)-L (i1)L个。
一共有 n − a [ i ] n-a[i] na[i]个数比 a [ i ] a[i] a[i]大。
右 边 &gt; a [ i ] 右边&gt;a[i] >a[i] 总 共 的 − 左 边 &gt; a [ i ] = ( n − a [ i ] ) − ( i − 1 − L ) 总共的-左边&gt;a[i]=(n-a[i])-(i-1-L) >a[i]=(na[i])(i1L)个。
然后就求出来了。。。

代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define N 200100
    #define mod 100000007ll
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define CLS(x) MEM(x,0)
    #define int long long
    class BIT
    {
        public:
            int tree[N];
            int len;
            void BuildTree(int _len)
            {
                len=_len;
                CLS(tree);
            }

            void Add(int pos,int x)
            {
                while(pos<=len)
                {
                    tree[pos]+=x;
                    pos+=(pos&(-pos));
                }
            }
            int Query(int pos)
            {
                int ans=0;
                while(pos>0)
                {
                    ans+=tree[pos];
                    pos-=(pos&(-pos));
                }
                return ans;
            }
    }T;

    int n,a[N];
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Input()
    {
        R1(n);
        for(int i=1;i<=n;++i)
        {
            R1(a[i]);
        }
    }

    void Soviet()
    {
        T.BuildTree(n);
        int ans=0;
        for(int i=1;i<=n;++i)
        {
            T.Add(a[i],1);
            int L=T.Query(a[i]-1);//左边有L个比a[i]小
            int R=(n-a[i])-(i-1-L);//右边有R个比a[i]大
            ans+=R*(R-1)/2-L*R;//一个计算当前的答案,一个去掉之前多算的答案
        }
        printf("%lld\n",ans%mod);
    }

    void IsMyWife()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        int t;R1(t);
        for(int i=1;i<=t;++i)
        {
            Input();
            printf("Case #%lld: ",i);
            Soviet();
        }
    }
    #undef int//long long
};
int main()
{
    Flandle_Scarlet::IsMyWife();
    return 0;
}

回到总题解界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值