题目是这样的:由于参加MooFest这个会议,使得有些奶牛耳聋了,我们现在就是想要知道要多少的声量使得所有牛两两之间都能听清,题目给出vi与xi分别代表最先听清所需的声量、这头奶牛在坐标上的位移。而题目让我们求得是,所需要的总和,也就是全体max(vi, vj)*abs(xi-xj)之和。
题面(后附上思路):
Every year, Farmer John's N (1 <= N <= 20,000) cows attend "MooFest",a social gathering of cows from around the world. MooFest involves a variety of events including haybale stacking, fence jumping, pin the tail on the farmer, and of course, mooing. When the cows all stand in line for a particular event, they moo so loudly that the roar is practically deafening. After participating in this event year after year, some of the cows have in fact lost a bit of their hearing.
Each cow i has an associated "hearing" threshold v(i) (in the range 1..20,000). If a cow moos to cow i, she must use a volume of at least v(i) times the distance between the two cows in order to be heard by cow i. If two cows i and j wish to converse, they must speak at a volume level equal to the distance between them times max(v(i),v(j)).
Suppose each of the N cows is standing in a straight line (each cow at some unique x coordinate in the range 1..20,000), and every pair of cows is carrying on a conversation using the smallest possible volume.
Compute the sum of all the volumes produced by all N(N-1)/2 pairs of mooing cows.
Input
* Line 1: A single integer, N
* Lines 2..N+1: Two integers: the volume threshold and x coordinate for a cow. Line 2 represents the first cow; line 3 represents the second cow; and so on. No two cows will stand at the same location.
Output
* Line 1: A single line with a single integer that is the sum of all the volumes of the conversing cows.
Sample Input
4
3 1
2 5
2 6
4 3
Sample Output
57
思路:
我们要求所有的值,既然要比较vi与vj的较大值,不妨就按照v的大小,按照升序排列,这样在vi之前的元素,他们要与vi建立联系,肯定是以vi的大小作为基底的。
接下来,我们对其中一个vi作为参考系来进行比较,我们接下来就要考虑的是xi的关系,对于xi,在其之前的元素,有比它大的,也有比它小的(就是不可能有相等的) ,这个时候可以把xi之前的那些{x}集合分成比xi小的以及比xi大的。那些比xi小的{x}可以用树状数组找到其个数a以及总量b,那么小于xi 的集合总大小就是a*xi-b;接下来,考虑大于xi的{x}集合,因为我们现在知道xi,那么就知道它是第i位,在此之前有(i-1)个元素,我们可以先弄一个前缀和,求得在此之前的到此位且不包含此位的元素的大小总和len[i],然后计算len[i]-b-(i-1-a)*xi即可求得大于xi的范围的总和。
完整代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
using namespace std;
typedef long long ll;
const int maxN=20005;
struct node
{
ll v,x;
node(ll a=0, int b=0) {v=a; x=b;}
}mp[maxN];
int N;
ll bita[maxN]; //用以记录小于x的点的数目,那么,我们接下来只需要知道小于x点的点的距离和即可
ll bitlen[maxN]; //记录下小于x的点的距离和,然后找到x之前元素的全体的前缀和即可求解关于i点的vi需求
ll qzsum[maxN]; //前缀和
bool cmp(node e1, node e2)
{
return e1.v==e2.v?(e1.x<e2.x):(e1.v<e2.v);
}
void update_a(ll i, ll x) //点数量的更新方式是把(xj, 1)放进去
{
while(i<=maxN)
{
bita[i]+=x;
i+=lowbit(i);
}
}
void update_len(ll i, ll x) //长度的更新方式是把(xj, xj)放进去,然后求的时候是求Query_len(xi)
{
while(i<=maxN)
{
bitlen[i]+=x;
i+=lowbit(i);
}
}
ll Query_a(ll i)
{
ll res=0;
while(i)
{
res+=bita[i];
i-=lowbit(i);
}
return res;
}
ll Query_len(ll i)
{
ll res=0;
while(i)
{
res+=bitlen[i];
i-=lowbit(i);
}
return res;
}
int main()
{
while(scanf("%d",&N)!=EOF)
{
ll ans=0; //这是最后所要求的答案
memset(mp, 0, sizeof(mp));
memset(bita, 0, sizeof(bita)); //计数的
memset(bitlen, 0, sizeof(bitlen)); //记长的
memset(qzsum, 0, sizeof(qzsum)); //记总长的
for(int i=1; i<=N; i++)
{
scanf("%lld%lld",&mp[i].v,&mp[i].x);
}
sort(mp+1, mp+1+N, cmp);
for(int i=1; i<=N; i++) qzsum[i]=qzsum[i-1]+mp[i-1].x; //求出除本身点之前的所有点的前缀和
update_a(mp[1].x, 1);
update_len(mp[1].x, mp[1].x);
for(int i=2; i<=N; i++)
{
ll a=Query_a(mp[i].x);
ll b=Query_len(mp[i].x);
ans+=(a*mp[i].x-b)*mp[i].v;
ans+=(qzsum[i]-b-(i-1-a)*mp[i].x)*mp[i].v;
update_a(mp[i].x, 1);
update_len(mp[i].x, mp[i].x);
//printf("%lld %lld\n",a,b);
}
printf("%lld\n",ans);
}
return 0;
}
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef long long ll;
/*
这道题我们需要记录这一串数之外,还要去找到(按照v【i】升序排列)x【i】,「 前缀和qzsum【i】 」去找到x【i】这个点所有距离之和
树状数组找到到这个点小于这个点的数的大小的和,并且还要用树状数组记录小于他的点的个数
然后就有大于这个x【i】点的数的和为:qzsum【i】-树状数组(x【i】)
大于这个点的个数由i-小于这个点的个数(x【i】)求得
*/
const int maxN=20005;
int N;
ll qzsum[maxN]; //前缀和
struct node
{
int v,x;
}a[maxN];
bool cmp(node e1, node e2)
{
return e1.v==e2.v?e1.x<e2.x:e1.v<e2.v;
}
int bit_lisX[maxN];
void update_sumoflittleX(int i) //求个数的
{
while(i<maxN)
{
bit_lisX[i]++;
i+=lowbit(i);
}
}
int Query_solx(int i) //求个数的
{
int res=0;
while(i)
{
res+=bit_lisX[i];
i-=lowbit(i);
}
return res;
}
ll bit_nolX[maxN];
void update_numoflittleX(int i, int x) //求小于目前这个数的和
{
while(i<maxN)
{
bit_nolX[i]+=x;
i+=lowbit(i);
}
}
ll Query_nolx(int i) //求小于目前这个数的和
{
ll res=0;
while(i)
{
res+=bit_nolX[i];
i-=lowbit(i);
}
return res;
}
int main()
{
while(scanf("%d",&N)!=EOF)
{
ll ans=0;
memset(qzsum, 0, sizeof(qzsum));
memset(bit_lisX, 0, sizeof(bit_lisX));
memset(bit_nolX, 0, sizeof(bit_nolX));
for(int i=1; i<=N; i++) scanf("%d%d",&a[i].v,&a[i].x);
sort(a+1, a+1+N, cmp);
update_sumoflittleX(a[1].x); //求小于某个数的数的个数的
update_numoflittleX(a[1].x, a[1].x); //已存入的小于某个数的总和
for(int i=2; i<=N; i++) //求前缀和
{
qzsum[i]=qzsum[i-1]+a[i-1].x;
int ssum=Query_solx(a[i].x); //小于当前值的数的个数
ll nnum=Query_nolx(a[i].x); //值
ans+=(ssum*a[i].x-nnum)*a[i].v;
ans+=(qzsum[i]-nnum-(i-1-ssum)*a[i].x)*a[i].v;
update_sumoflittleX(a[i].x); //更新操作
update_numoflittleX(a[i].x, a[i].x);
}
printf("%lld\n",ans);
}
return 0;
}