线段树
![](http://acm.fzu.edu.cn/image/problem.gif)
Accept: 302 Submit: 1633
Time Limit: 1500 mSec Memory Limit : 32768 KB
Problem Description
怀特先生是一名研究地震的科学家,最近他发现如果知道某一段时间内的地壳震动能量采样的最小波动值之和,可以有效地预测大地震的发生。
假设已知一段时间的n次地壳震动能量的采样值为a1,a2,…an,那么第i 次采样的最小波动值为min{|ai-aj| | i<j<=n},即第i 次采样的最小波动值是其后n-i次采样值与第i次采样值之差的绝对值中最小的值,特别地,第n次采样的最小波动值为an。
请编写一个程序计算这n次采样的最小波动值之和。
Input
本题有多组输入数据,你必须处理到EOF为止
输入数据第一行有一个数n(1<=n<=105) ,表示采样的次数。
第二行有n个整数,表示n次地壳震动能量的采样值a1,a2,…an (0<=ai<=107 )。
Output
输出n次采样的最小波动值之和。
Sample Input
4
2 0 3 10
Sample Output
21
参考别人的思路做的,用到了线段树求和
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> //#include <cmath> #define lowbit(x) x&(-x) using namespace std; const int maxn=100010; int a[maxn],b[maxn],sum[maxn],k,tim[maxn]; bool flag[maxn]; void insert(int pos,int d) { for(int i=pos;i<k;i+=lowbit(i)) sum[i]+=d; } int query(int pos) { int s=0; for(int i=pos;i>0;i-=lowbit(i)) s+=sum[i]; return s; } int bin(int key,int l,int r) { int m; while(l<=r) { m=(l+r)>>1; if(b[m]==key) return m; else if(b[m]<key) l=m+1; else r=m-1; } } int bin1(int key,int l,int r) { int m,ret=1; while(l<=r) { m=(l+r)>>1; int ss=query(m); if(ss>=key) { ret=m;r=m-1; } else l=m+1; } return ret; } int abs(int key) { if(key>0) return key; return -key; } int main() { int n,pos; while(scanf("%d",&n)==1) { for(int i=1;i<=n;i++) { scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+n+1); memset(sum,0,sizeof(sum)); k=2; tim[1]=1; for(int i=2;i<=n;i++) if(b[i]!=b[i-1]) b[k++]=b[i],tim[k-1]=1; else tim[k-1]++; for(int i=1;i<k;i++) insert(i,tim[i]); // cout<<endl; long long ans=a[n]; // cout<<ans<<endl; for(int i=1;i<n;i++) { int pos=bin(a[i],1,k-1); // cout<<pos<<" "; insert(pos,-1); int rank=query(pos); // cout<<rank<<" "; int Min=99999999; if(rank!=0) { int pos1=bin1(rank,1,k-1); Min=min(Min,abs(a[i]-b[pos1])); } if(rank!=n-i) { int pos2=bin1(rank+1,1,k-1); Min=min(Min,abs(a[i]-b[pos2])); } ans+=Min; } printf("%lld\n",ans); } return 0; }