您的位置:首页 > 产品设计 > UI/UE

BZOJ 1367: [Baltic2004]sequence

2015-05-11 19:01 411 查看


思考这道题目的时候毫无思路,而且数据范围很像是贪心题啊。其实我们可以考虑一些极端的情况,如何这个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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: