题意:一群耳背的牛,每头牛有一个音量阈值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); }