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 }

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

没有人能阻止我前进的步伐,除了我自己!
原文地址:https://www.cnblogs.com/proverbs/p/2710495.html