zoukankan      html  css  js  c++  java
  • POJ 1990 树状数组

    题意:题意:FJ有n头牛,排列成一条直线(不会在同一个点),给出每头牛在直线上的坐标x。另外,每头牛还有一个自己的声调v,如果两头牛(i和j)之间想要沟通的话,它们必须用同个音调max(v[i],v[j]),沟通起来消耗的能量为:max(v[i],v[j]) * 它们之间的距离。问要使所有的牛之间都能沟通(两两之间),总共需要消耗多少能量。

    这个题看着数据量挺吓人的,但是只要肯动笔,这题其实很水的。

    思路:

    这个题目n^2的肯定能做,TLE呗,所以我们思考如何降低复杂度。

    开始动笔:

    设消耗的总能量为ans,则ans=sigma(max(v[i],v[j])*abs(x[j]-x[i])) 1<=i<j<=n

    这个式子是没法化简的,原因有两个:

    ①包含max函数

    ②包含绝对值

    那既然他们妨碍我们,我们就要想办法把他们去掉,首先对付max函数

    这个大家应该很容易想到,因为i<j,排序一下不就保证v[i]<v[j]了吗?

    按照x由小到大排序

    ans=sigma(v[j]*abs(x[j]-[i]))  1<=i<j<=n

    现在就要专心对付绝对值,想必大家初中老师都告诉过大家如何去绝对值——分类讨论

    ans=sigma(v[j]*(fn[j]*x[j]-fx[j]+bx[j]-bn[j]*x[j]))    1<=i<j<=n    1<=k<j<=n

    其中fn[j]为满足v[j]>=v[i]且x[i]<=x[j]的奶牛数量,即排序后在j之前的坐标小于j的奶牛数量

    bn[j]为满足v[j]>=v[i]且x[i]>x[j]的奶牛数量,即排序后在j之前的坐标大于j的奶牛数量

    fx[j]=fn[j]*x[i]

    bx[j]=bn[j]*x[k]

    这样推到基本就完成了,因为fn和bn都是动态变化的,所以我们要找一种数据结构帮助我们很快的查询——树状数组(应该能想到了)

    将数组下标与奶牛的坐标对应,建立两个树状数组:

    ①num[i],存储i坐标是否已经放置了奶牛,很方便通过前缀和算出fn[j],然后bn[j]=j-1-fn[j]

    ②sx[i],存储i坐标的奶牛的坐标,可以计算出fx[j],然后bx[j]=sumsx-fx[j]

    其中sumsx是前i-1个奶牛的坐标的和

    具体还是看代码吧~

    表达能力不行,只能描述到这种地步了。。

    View Code
     1 #include <cstdio>
     2 #include <cstring>
     3 #include <cstdlib>
     4 #include <algorithm>
     5 
     6 #define N 25000
     7 
     8 using namespace std;
     9 
    10 struct COW
    11 {
    12     __int64 v,x;
    13 }cow[N];
    14 
    15 __int64 n,num[N],sx[N];
    16 
    17 inline bool cmp(const COW &a,const COW &b)
    18 {
    19     return a.v<b.v;
    20 }
    21 
    22 void read()
    23 {
    24     for(__int64 i=1;i<=n;i++) scanf("%I64d%I64d",&cow[i].v,&cow[i].x);
    25     sort(cow+1,cow+1+n,cmp);
    26     memset(sx,0,sizeof sx);
    27     memset(num,0,sizeof num);
    28 }
    29 
    30 inline __int64 lowbit(__int64 x)
    31 {
    32     return x&-x;
    33 }
    34 
    35 inline void updata(__int64 *a,__int64 x,__int64 dt)
    36 {
    37     while(x<N)
    38     {
    39         a[x]+=dt;
    40         x+=lowbit(x);
    41     }
    42 }
    43 
    44 inline __int64 getsum(__int64 *a,__int64 x)
    45 {
    46     __int64 rt=0;
    47     while(x)
    48     {
    49         rt+=a[x];
    50         x-=lowbit(x);
    51     }
    52     return rt;
    53 }
    54 
    55 void go()
    56 {
    57     __int64 ans=0;
    58     __int64 sumsx=0;//前i头牛的坐标和 
    59     __int64 forwardnum,backwardnum;//i以前的牛的个数 ,i以后的牛的个数 
    60     __int64 forwardsx,backwardsx;//i以前的牛坐标总和 ,i以后的牛坐标总和 
    61     for(__int64 i=1;i<=n;i++)
    62     {
    63         forwardnum=getsum(num,cow[i].x);//num没有更新呢! 
    64         backwardnum=i-1-forwardnum;
    65         
    66         forwardsx=getsum(sx,cow[i].x);
    67         backwardsx=sumsx-forwardsx;//sumsx没有更新呢! 
    68         
    69         ans+=cow[i].v*(forwardnum*cow[i].x-forwardsx+backwardsx-backwardnum*cow[i].x);
    70         
    71         updata(sx,cow[i].x,cow[i].x);
    72         updata(num,cow[i].x,1);
    73         sumsx+=cow[i].x;
    74     }
    75     printf("%I64d\n",ans);
    76 }
    77 
    78 int main()
    79 {
    80     while(scanf("%I64d",&n)!=EOF)
    81     {
    82         read();
    83         go();
    84     }
    85     return 0;
    86 }

    决定以后多写题解,一是锻炼我的表达能力,二是对题目增加更深层次的认识!

    没有人能阻止我前进的步伐,除了我自己!
  • 相关阅读:
    ajax专题
    luogu P1346 电车 最短路
    luogu P1462 通往奥格瑞玛的道路 最短路
    luogu P1328 生活大爆炸版石头剪刀布
    luogu P1315 联合权值 枚举
    luogu P1156 垃圾陷阱 背包问题
    luogu P1217 回文质数 枚举
    luogu P3650 滑雪课程设计 枚举
    luogu1209 修理牛棚 贪心
    luogu P1223 排队接水 贪心
  • 原文地址:https://www.cnblogs.com/proverbs/p/2710495.html
Copyright © 2011-2022 走看看