题意:一群耳背的牛,每头牛有一个音量阈值xi。当两头牛i,j交流的时候,交流的最小声音为max{x[i],x[j]}*他们之间的距离。现在有n头牛,求他们之间两两交流最少要的音量和。
思路:n的大小为20000显然不要n^2的算法。思路为首先按照阈值x对牛排序。那么新顺序下两头牛交流他们的max{x[i],x[j]}必为后面的牛的阈值,接下来就是快速求出当前牛和排在它前面所有牛的距离之和。需要用到两个树状数组,一个存放牛在特定距离的个数,另一个存放距离值。细节见代码。
poj2231:依然是牛之间谈话,但是比1990简单。a对b说话,音量必须等于两点之间的距离,问两两之间谈话的总音量。题目抽象相当于给出直线上n个点,求n(n-1)点对之间的距离之和。做法:先排序,然后从左到右一个一个求每个点到其左边点集的距离之和。维护一个1~k的下标之和即可。需要注意中间计算会不会超过int,如果超,勿忘强制转换。
1990代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 20005
long long t1[N],t2[N];
struct point{
int id,x;
}p[N];
int n;
int cmp(struct point a,struct point b){
return a.x < b.x;
}
int lowbit(int x){
return x&(-x);
}
void add(long long* tree,int i,int x){
for(int j = i;j<=N-5;j+=lowbit(j))
tree[j] += x;
}
long long sum(long long* tree,int i){
long long res = 0;
for(int j = i;j>=1;j-=lowbit(j))
res += tree[j];
return res;
}
int main(){
int i;
long long res = 0,j;
clc(t1, 0);
clc(t2, 0);
scanf("%d",&n);
for(i = 1;i<=n;i++)
scanf("%d %d",&p[i].x,&p[i].id);
sort(p+1,p+1+n,cmp);
add(t1,p[1].id,1);//t1是个数
add(t2,p[1].id,p[1].id);//t2是距离
for(i = 2;i<=n;i++){
j = 0;
j += sum(t1,p[i].id-1)*p[i].id-sum(t2,p[i].id-1);//牛i与之前距离号小于它的所有牛的距离差之和
j += sum(t2,N-5)-sum(t2,p[i].id)-(sum(t1,N-5)-sum(t1,p[i].id))*p[i].id;//牛i与之前距离号大于它的所有牛的距离差之和
res += j*p[i].x;
add(t1,p[i].id,1);
add(t2,p[i].id,p[i].id);
}
printf("%lld\n",res);
}
2231:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 10005
int s[N];
int n;
int main(){
int i;
long long j,res=0;
scanf("%d",&n);
for(i = 0;i<n;i++)
scanf("%d",&s[i]);
sort(s,s+n);
j = s[0];
for(i = 1;i<n;i++){
res += (long long)s[i]*i - j;
j += s[i];
}
printf("%lld\n",res<<1);
}