BZOJ 1588 [HNOI2002]营业额统计(双向链表)
【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=1588
【题目大意】
给出一个数列,对于每个数,选择其前面的某个数作差取绝对值,
使得所有差的绝对值的和最小,第一个数统计其值作为该数的答案。
【题解】
题目等价于在一个数前面寻找最接近的一个数,
显然这是一个平衡树求前继和后继能解决的为问题,
然而因为题目可以离线,我们考虑编码复杂度更低的基础数据结构,
我们将所有数据排序,构造双向链表,那么单节点左右就是与其差值最小的数,
我们按数列逆序处理节点,当计算完一个节点之后将其从链表中删除即可。
由于链表的有序性,使得剩余节点始终满足左右节点为当前剩余点中与该节点差值最小的点,
复杂度O(nlogn).
【代码】
#include <cstdio> #include <algorithm> using namespace std; const int N=500000; struct data{int l,r,num,id;}p[N]; bool cmp(data a,data b){return a.num<b.num;} int n,pos[N]; int main(){ while(~scanf("%d",&n)){ for(int i=1;i<=n;i++)scanf("%d",&p[i].num),p[i].id=i; sort(p+1,p+n+1,cmp); for(int i=1;i<=n;i++){ p[i].l=i-1,p[i].r=i+1; pos[p[i].id]=i; }p[n].r=0; long long ans=p[pos[1]].num; for(int i=n;i>1;i--){ int x=pos[i]; if(p[x].l&&p[x].r){ ans+=min(p[p[x].r].num-p[x].num,p[x].num-p[p[x].l].num); p[p[x].l].r=p[x].r; p[p[x].r].l=p[x].l; }else if(p[x].l){ ans+=p[x].num-p[p[x].l].num; p[p[x].l].r=0; }else{ ans+=p[p[x].r].num-p[x].num; p[p[x].r].l=0; } }printf("%lld\n",ans); }return 0; }
愿你出走半生,归来仍是少年