洛谷5094 & poj 1990 [USACO2004OPEN]MooFest 题解(树状数组)

本文探讨了一道经典的算法题目,涉及对奶牛的价值和位置进行排序和计算,通过使用树状数组来优化求解过程,实现高效计算所有奶牛对的最大值与距离乘积之和。

原题链接:
洛谷
poj

题意简述

给定 n ( &lt; = 2 e 4 ) n(&lt;=2e4) n(<=2e4),和 n n n个奶牛。每个奶牛有两个值, v ( 每 个 &lt; = 2 e 4 ) v(每个&lt;=2e4) v(<=2e4), x i ( 每 个 &lt; = 2 e 4 ) x_i(每个&lt;=2e4) xi(<=2e4)。求对于所有的奶牛 ( a , b ) ( 1 &lt; = a &lt; b &lt; = n ) (a,b)(1&lt;=a&lt;b&lt;=n) (a,b)(1<=a<b<=n) m a x ( v a , v b ) ∗ ( ∣ x a − x b ∣ ) max(v_a,v_b)*(|x_a-x_b|) max(va,vb)(xaxb)的值的和。

思路

这题看到要求的式子里有一个 m a x max max,一个绝对值,第一眼看到肯定是没有思路的。但是我们要想想,最大值怎么拆?绝对值怎么拆?

最大值是好做的,我们只要把 n n n个奶牛按 v v v值排序就好做了。枚举 i i i,考虑 [ 1 , i − 1 ] [1,i-1] [1,i1]之间的答案,由于是排好序的,前面的 v v v值肯定不会超过 v i v_i vi,那么取 m a x max max就一定是 v i v_i vi了。

那么绝对值呢?通常我们要求两个数相减的绝对值的时候,要讨论两个数哪个大。还是枚举 i i i,讨论:

  1. x i x_i xi是较大的数。
    此时,对于前面所有的 j j j满足: x j &lt; = x i x_j&lt;=x_i xj<=xi,要求和
    ( x i − x j ) (x_i-x_j) (xixj),乘以 v i v_i vi,就是对答案的贡献了。也就是 ∑ j = 1 i − 1 [ x j &lt; = x i ] ( x i − x j ) \sum\limits_{j=1}^{i-1}[x_j&lt;=x_i](x_i-x_j) j=1i1[xj<=xi](xixj)。然后我们发现 x i x_i xi是不变的数,珂以提出来。但是系数呢?就是 [ 1 , i − 1 ] [1,i-1] [1,i1]中满足 x j &lt; = x i x_j&lt;=x_i xj<=xi的个数。设其为 C C C(xk)。原式化为:
    C ∗ x i − ∑ j = 1 i − 1 [ x j &lt; = x i ] x j C*x_i-\sum\limits_{j=1}^{i-1}[x_j&lt;=x_i]x_j Cxij=1i1[xj<=xi]xj。那么此时我们发现,还要求一个东西,就是 ∑ j = 1 i − 1 [ x j &lt; = x i ] x j \sum\limits_{j=1}^{i-1}[x_j&lt;=x_i]x_j j=1i1[xj<=xi]xj。设其为 S S S。( C C C S S S的求法过会再说,先讨论)

  2. x i x_i xi是较小的数
    此时,对于前面所有的 j j j满足: x j &gt; x i x_j&gt;x_i xj>xi(为了避免和1.重复,这里不取等),求和:
    ( x j − x i ) (x_j-x_i) (xjxi),乘以 v i v_i vi,就是贡献。也就是 ∑ j = 1 i − 1 [ x j &gt; x i ] ( x j − x i ) \sum\limits_{j=1}^{i-1}[x_j&gt;x_i](x_j-x_i) j=1i1[xj>xi](xjxi)。照例还是提 x i x_i xi,但是系数呢?不会又要再设一个数出来吧。。。
    别担心,完全不用。前 i − 1 i-1 i1个数中,要么就是 x j &gt; x i x_j&gt;x_i xj>xi,要么就是 x j &lt; = x i x_j&lt;=x_i xj<=xi。我们知道了 x j &lt; = x i x_j&lt;=x_i xj<=xi的个数,拿总数 i − 1 i-1 i1去减掉它,就是满足 x j &gt; x i x_j&gt;x_i xj>xi的个数了,即 ( i − 1 − C ) (i-1-C) (i1C)个。
    类似的思路,式子变为: ∑ j = 1 i − 1 [ x j &gt; x i ] x j − ( i − 1 − C ) ∗ x \sum\limits_{j=1}^{i-1}[x_j&gt;x_i]x_j-(i-1-C)*x j=1i1[xj>xi]xj(i1C)x。现在我们要求 ∑ j = 1 i − 1 [ x j &gt; x i ] x j \sum\limits_{j=1}^{i-1}[x_j&gt;x_i]x_j j=1i1[xj>xi]xj,如何求呢?类似得到 i − 1 − C i-1-C i1C的思路,其实我们只要用前 i − 1 i-1 i1个数的前缀和,减去 S S S,就是这里的这个东西了。我们一边枚举 i i i,一边记录当前 ( 1 , i − 1 ) (1,i-1) (1,i1)的和。设这个和为 a l l all all,那么此时的式子就是: ( a l l − S ) − ( i − 1 − C ) ∗ x (all-S)-(i-1-C)*x (allS)(i1C)x

现在差不多知道式子了。我们很清楚 a l l all all的求法,就是边枚举边记录。问题来了:怎么求 C C C S S S

维护一个权值树状数组 T c , T s Tc,Ts Tc,Ts T c [ x ] Tc[x] Tc[x]表示:满足 a i = x a_i=x ai=x的数有多少个。 T s [ x ] Ts[x] Ts[x]表示 a i = x a_i=x ai=x的数的 x x x值和。然后 C C C就是 T c [ 1 ] + T c [ 2 ] . . . + T c [ x i ] Tc[1]+Tc[2]...+Tc[x_i] Tc[1]+Tc[2]...+Tc[xi] S S S就是 T s [ 1 ] + T s [ 2 ] . . . + T s [ x i ] Ts[1]+Ts[2]...+Ts[x_i] Ts[1]+Ts[2]...+Ts[xi]。(由于是 &lt; = &lt;= <=,珂以取到 x i x_i xi。)我们发现是求前缀和的工作,珂以用树状数组快速求出。

然后要讲的就是次序问题。首先我们要先作询问操作,求出 S , C S,C S,C的值,统计答案,然后再记录 T c , T s Tc,Ts Tc,Ts a l l all all的值。尤其是 a l l all all,一定要写在后面,这样才能保证我们前面再调用的时候还只有前 i − 1 i-1 i1个数的和。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
namespace Flandle_Scarlet
{
    #define int long long
    #define N 200100
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)
    struct node
    {
        int v,x;
    }a[N];
    bool cmp_v(node a,node b){return a.v<b.v;}
    int 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)
        {
            int v,x;
            R1(v),R1(x);
            a[i]=(node){v,x};
        }
    }

    class BIT
    {
        public:
            int tree[N];
            int len;
            void BuildTree(int _len)
            {
                len=_len;
                FK(tree);
            }
            void Add(int pos,int val)
            {
                for(int i=pos;i<=len;i+=(i&(-i)))
                {
                    tree[i]+=val;
                }
            }
            int Query(int pos)
            {
                int ans=0;
                for(int i=pos;i>0;i-=(i&(-i)))
                {
                    ans+=tree[i];
                }
                return ans;
            }
    }Tc,Ts;
    void Soviet()
    {
        Tc.BuildTree(200000);
        Ts.BuildTree(200000);
        sort(a+1,a+n+1,cmp_v);
        int ans=0;
        int all=0;
        for(int i=1;i<=n;++i)
        {
            int x=a[i].x,v=a[i].v;
            int lcnt=Tc.Query(x),lsum=Ts.Query(x);
            ans+=(x*lcnt-lsum)*a[i].v;
            ans+=((all-lsum)-(i-1-lcnt)*x)*a[i].v;
            Tc.Add(x,1);
            Ts.Add(x,x);
            all+=a[i].x;
        }
        printf("%lld\n",ans);
    }
    void IsMyWife()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        Input();
        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、付费专栏及课程。

余额充值