题目链接:http://poj.org/problem?id=1990
题意:有n个奶牛站成一排,每只奶牛都有两个值,v和pos,如果两只奶牛i和j想传话,需要消耗max(v[i],v[j]) * | pos[i]-pos[j] |
现在求n个奶牛之间互相都传话一次,一共需要消耗多少?
思思路:答案是n*(n-1)/2个数的和,但是如果我们一个个求的话,会超时。
首先,我们规定每只奶牛贡献的答案是,它与比它v值小的奶牛所对话产生的值,这样的话可以让v值固定,而且也可以把所有情况考虑进去。每只奶牛所产生的答案变成v*(∑|pos-pos[j]|)v[j]<=v ,如果把所有的j分为左右两侧计算的话,把符合的奶牛分成左边和右边,那么就可以去掉绝对值,变成 v*( n*pos - ∑pos[j'] )+ v*( ∑pos[j'']-n*pos )
这样我们就可以用树状数组去优化这个求和。
根据公式,我们需要求距离和还有符合个数,所以我们用两个树状数组sum,num分别维护。
先统计左侧的答案:我们每次遇到一只奶牛i,那么它的左侧贡献的答案为:v[i]*( query(v[i],num)*pos[i] - query(v[i],sum) ), 累加完答案后更新树状数组。
同理,再统计右侧的答案。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
#define rep(i,j,k) for(int i = j; i <= k; i++ )
#define Rrep(i,j,k) for(int i = j; i >= k; i-- )
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
const int maxn =200000;
int n;
struct node
{
int pos;
int v;
}a[maxn+10];
bool cmp(node a,node b)
{
return a.pos < b.pos;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int k,int a[])
{
while( x <= maxn )
{
a[x]+=k;
x+=lowbit(x);
}
}
int query(int x,int a[])
{
int ans = 0;
while(x)
{
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int sum[maxn+10];
int num[maxn+10];
int main()
{
cin>>n;
rep(i,1,n) scanf("%d %d",&a[i].v,&a[i].pos);
sort(a+1,a+1+n,cmp);
LL ans = 0;
Clean(sum,0);
Clean(num,0);
rep(i,1,n) //计算左侧答案考虑相等情况
{
ans+= a[i].v *( (LL)a[i].pos*query( a[i].v , num ) - query( a[i].v , sum ) );
add( a[i].v,1,num );
add( a[i].v,a[i].pos,sum );
}
Clean(sum,0);
Clean(num,0);
Rrep(i,n,1) //计算右侧答案,只考虑严格大于的情况,否则会加重
{
ans+= a[i].v *( query( a[i].v -1 , sum ) - (LL)a[i].pos*query( a[i].v - 1 , num ) );
add( a[i].v,1,num );
add( a[i].v,a[i].pos,sum );
}
cout<<ans<<endl;
return 0;
}