您的位置:首页 > 其它

【IOI2014】bzoj4367 holiday

2017-07-05 22:00 211 查看
最优方案一定是先向一边走,然后掉头回来,再向另一边走停在最远处,剩下的时间取走一路上的最大值。

我们用f[0/1][0/1][i]表示用掉时间i,向左或向右,回到出发点或不回到,得到的最大收益。不难发现,这四个值求法类似。

如果知道到达的最远点的话,能取走的位置的个数是确定的。我么可以用主席树查询区间k大值和来O(logn)求出答案。而显然决策点是单调的,也就是说,时间越长,走到的最远点越远。于是我们可以分治求解,定义solve(L,R,l,r)表示求解[L,R]的f值,决策点在[l,r]。令mid=(L+R)/2,先暴力枚举决策点求出mid的最优决策x,这一步是O(nlogn)的,于是递归下去的时候决策点也分成了两部分。分治结构总共有O(logn)层,每一层求解的复杂度之和是O(nlogn),所以最后的复杂度是O(nlog2n)。

注意起点位置是不能被两边同时取走的,因此需要特殊处理。好像只有规定往回走的那一边取走起点才是对的,我也不知道为什么。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100010,maxt=2800000;
#define LL long long
int rd()
{
int x=0;
char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9')
{
x=x*10+c-'0';
c=getchar();
}
return x;
}
int a[maxn],val[maxn],size[maxt],lson[maxt],rson[maxt],rt[maxn],n,s,m,l,tot;
LL sum[maxt],f[2][2][250010];
void build(int &u,int bro,int L,int R,int x)
{
u=++tot;
size[u]=size[bro]+1;
sum[u]=sum[bro]+val[x];
if (L==R) return;
int mid=(L+R)/2;
if (x<=mid)
{
build(lson[u],lson[bro],L,mid,x);
rson[u]=rson[bro];
}
else
{
lson[u]=lson[bro];
build(rson[u],rson[bro],mid+1,R,x);
}
}
LL qry(int u,int v,int L,int R,int k)
{
if (k>=size[u]-size[v]) return sum[u]-sum[v];
if (L==R) return k*val[L];
int mid=(L+R)/2;
if (k<=size[rson[u]]-size[rson[v]]) return qry(rson[u],rson[v],mid+1,R,k);
return sum[rson[u]]-sum[rson[v]]+qry(lson[u],lson[v],L,mid,k-(size[rson[u]]-size[rson[v]]));
}
LL cal(int f1,int f2,int t,int p)
{
int u,v,x;
if (f1) u=s+f2,v=p;
else u=p,v=s-f2;
x=t-abs(p-s)*(f2?1:2);
if (x<=0||u>v) return 0;
return qry(rt[v],rt[u-1],1,l,x);
}
void solve(int f1,int f2,int L,int R,int l,int r)
{
if (L>R) return;
int mid=(L+R)/2,p=l;
LL x;
f[f1][f2][mid]=cal(f1,f2,mid,l);
for (int i=l+1;i<=r;i++)
{
x=cal(f1,f2,mid,i);
if (x>f[f1][f2][mid])
{
p=i;
f[f1][f2][mid]=x;
}
}
if (f1)
{
solve(f1,f2,L,mid-1,l,p);
solve(f1,f2,mid+1,R,p,r);
}
else
{
solve(f1,f2,L,mid-1,p,r);
solve(f1,f2,mid+1,R,l,p);
}
}
int main()
{
//freopen("f.in","r",stdin);
//freopen("f.out","w",stdout);
LL ans=0;
n=rd();
s=rd()+1;
m=rd();
for (int i=1;i<=n;i++) a[i]=val[i]=rd();
sort(val+1,val+n+1);
l=unique(val+1,val+n+1)-val-1;
for (int i=1;i<=n;i++)
{
a[i]=lower_bound(val+1,val+l+1,a[i])-val;
build(rt[i],rt[i-1],1,l,a[i]);
}
solve(0,0,0,m,1,s);
solve(0,1,0,m,1,s);
solve(1,0,0,m,s,n);
solve(1,1,0,m,s,n);
for (int i=0;i<=m;i++)
{
ans=max(ans,f[0][0][i]+f[1][1][m-i]);
ans=max(ans,f[0][1][i]+f[1][0][m-i]);
}
printf("%lld\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: