BZOJ 1367: [Baltic2004]sequence

来源:互联网 发布:mysql 排序 编辑:程序博客网 时间:2024/05/29 14:23

这里写图片描述

思考这道题目的时候毫无思路,而且数据范围很像是贪心题啊。其实我们可以考虑一些极端的情况,如何这个t序列就是递增的,那个直接zi=ti就好了,如果这个序列是递减的,我们可以让z序列全部等于t序列的中位数,这样显然是最优的。(证明略)但题目中z序列递增,所以做一个变换,让所有的zi-i,这样就保证递增了。接下来我们将递减序列进行合并,最后得到的就都是递增序列,直接赋值就好了,在将递减序列合并的时候,还要对应地合并这些序列的中位数,这项操作如何快速完成呢?想到利用可并堆(真不好想啊…),堆里存放的是这个序列前一半的元素,两个堆合并时保留合并的大序列的前一半元素,但这样好像并不一定得到中位数。比如将5 6 7 8 9和1 2 3 4合并,就得到1 2 5 6 7,但这样的情况并不会发生,因为我们每发现一个递减序列就合并, 很容易证得。

总结一下算法的过程,首先将输入序列进行变换,然后按顺序加入,如何发现存在两个序列的中位数递减,则可以进行合并,最后得到了递增的中位数序列,直接赋值相减。
Tips:absi2011的配对堆比我的左偏树高明得不知道到哪里去了。

#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>using namespace std;const int maxn=1000000+10;int l[maxn],r[maxn],root[maxn],a[maxn],w[maxn],left1[maxn],right1[maxn],dis[maxn],v[maxn],n,tot;int num[maxn],cnt[maxn];int merge(int x,int y){  if(!x||!y) return x+y;  if(v[x]<v[y]) swap(x,y);  r[x]=merge(r[x],y);  if(dis[l[x]]<=dis[r[x]]) swap(l[x],r[x]);  dis[x]=dis[r[x]]+1;  return x;}int top(int x){  return v[x];}void pop(int &x){  x=merge(l[x],r[x]);}int main(){  //freopen("1367.in","r",stdin);  //freopen("1367.out","w",stdout);  scanf("%d",&n);  for(int i=1;i<=n;i++)   {    scanf("%d",&a[i]);    a[i]-=i;  }  for(int i=1;i<=n;i++)  {    root[++tot]=i;v[i]=a[i];l[i]=r[i]=dis[i]=0;    num[tot]=cnt[tot]=1;left1[tot]=i;right1[tot]=i;    while(top(root[tot-1])>top(root[tot])&&tot>1)    {      tot--;root[tot]=merge(root[tot],root[tot+1]);      num[tot]+=num[tot+1];cnt[tot]+=cnt[tot+1];right1[tot]=right1[tot+1];      while(cnt[tot]>(num[tot]+1)/2)      {        pop(root[tot]);        cnt[tot]--;      }    }  }  long long ans=0;  for(int i=1;i<=tot;i++)    for(int j=left1[i];j<=right1[i];j++)      ans+=abs(top(root[i])-a[j]);  printf("%lld\n",ans);  return 0;}
0 0
原创粉丝点击