您的位置:首页 > 其它

BZOJ 4367 [IOI2014]holiday假期 分治 主席树

2016-05-13 18:34 351 查看
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4367

题目大意:

有n个标号0到n−1的城市,从标号为start的城市出发旅行d天

每天可以选择参观所在城市的景点或者移动到相邻城市,求最多可以看多少景点

题解:

不难看出,若想尽可能看更多的景点,改变旅行方向的次数至多为1

可以分别预处理出: ①只向左走②只向右走③向左走并返回④向右走并返回 能参观的最大景点数

将它们拼接起来取最大值就能得到最终的答案了

处理出他们的方法极为类似,现在只研究如何求出只向右走能参观的最大景点数

用a[i]表示只向右走i天能参观到的最大景点数

我们可以枚举走到了城市x,那么我们就可以参观start到x这段区间中的i−x+start个城市的景点

显然我们要参观景点数最多的i−x+start个城市,用主席树可以轻松求出其中共有多少个景点

最后从所有st≤x≤n−1得到的答案中取最大值即可,但是时间复杂度O(n2log2n)难以接受

观察发现对于旅行天数的增加,取最优值得决策点不会向左移动

于是我们可以分治解决问题,每一次在决策范围内枚举得出a[mid]与决策点的位置,时间复杂度为O(nlog22n)

最后,不离散化MLE(╯°□°)╯︵┻━┻

代码:

#include <bits/stdc++.h>
using namespace std;
#define MAXN 100005
typedef long long ll;
int n,st,D;
int aa[MAXN];
pair<int,int*>bb[MAXN];
int tt;
int cc[MAXN];
struct Node
{
int num;
ll sum;
int l,r;
}tr[MAXN*20];
int root[MAXN],tot;
void Update(int val,int l,int r,int &rt)
{
tr[++tot]=tr[rt];rt=tot;
tr[rt].num++;
tr[rt].sum+=cc[val];
if(l==r) return;
int mid=(l+r)>>1;
if(val<=mid) Update(val,l,mid,tr[rt].l);
else Update(val,mid+1,r,tr[rt].r);
}
ll Query(int k,int l,int r,int L,int R)
{
if(k<=0) return 0;
if(tr[R].num-tr[L].num<=k) return tr[R].sum-tr[L].sum;
if(l==r) return (tr[R].sum-tr[L].sum)/(tr[R].num-tr[L].num)*k;
int mid=(l+r)>>1;
if(tr[tr[R].r].num-tr[tr[L].r].num>=k) return Query(k,mid+1,r,tr[L].r,tr[R].r);
else return tr[tr[R].r].sum-tr[tr[L].r].sum+Query(k-tr[tr[R].r].num+tr[tr[L].r].num,l,mid,tr[L].l,tr[R].l);
}
ll a[MAXN*2],b[MAXN*2],c[MAXN*3],d[MAXN*3];
void Geta(int l,int r,int L,int R)
{
if(l>r) return;
int mid=(l+r)>>1;
int w=L;a[mid]=Query(mid-L+st,1,tt,root[st-1],root[L]);
for(int i=L+1;i<=R;i++)
{
ll tmp=Query(mid-i+st,1,tt,root[st-1],root[i]);
if(tmp>a[mid]) a[mid]=tmp,w=i;
}
Geta(l,mid-1,L,w);Geta(mid+1,r,w,R);
}
void Getb(int l,int r,int L,int R)
{
if(l>r) return;
int mid=(l+r)>>1;
int w=R;b[mid]=Query(mid-st+R,1,tt,root[R-1],root[st]);
for(int i=R-1;i>=L;i--)
{
ll tmp=Query(mid-st+i,1,tt,root[i-1],root[st]);
if(tmp>b[mid]) b[mid]=tmp,w=i;
}
Getb(l,mid-1,w,R);Getb(mid+1,r,L,w);
}
void Getc(int l,int r,int L,int R)
{
if(l>r) return;
int mid=(l+r)>>1;
int w=R;c[mid]=Query(mid-st-st+R+R+1,1,tt,root[R-1],root[st-1]);
for(int i=R-1;i>=L;i--)
{
ll tmp=Query(mid-st-st+i+i+1,1,tt,root[i-1],root[st-1]);
if(tmp>c[mid]) c[mid]=tmp,w=i;
}
Getc(l,mid-1,w,R);Getc(mid+1,r,L,w);
}
void Getd(int l,int r,int L,int R)
{
if(l>r) return;
int mid=(l+r)>>1;
int w=L;d[mid]=Query(mid-L-L+st+st+1,1,tt,root[st],root[L]);
for(int i=L+1;i<=R;i++)
{
ll tmp=Query(mid-i-i+st+st+1,1,tt,root[st],root[i]);
if(tmp>d[mid]) d[mid]=tmp,w=i;
}
Getd(l,mid-1,L,w);Getd(mid+1,r,w,R);
}
int main()
{
scanf("%d%d%d",&n,&st,&D);
for(int i=0;i<n;i++)scanf("%d",aa+i),bb[i]=make_pair(aa[i],aa+i);
sort(bb,bb+n);
for(int i=0;i<n;i++)
{
if(!i||bb[i].first!=bb[i-1].first)
cc[++tt]=bb[i].first;
*bb[i].second=tt;
}
for(int i=0;i<n;i++)
{
if(i) root[i]=root[i-1];
Update(aa[i],1,tt,root[i]);
}
Geta(0,n*2,st,n-1);
Getb(0,n*2,0,st);
if(st>0) Getc(0,n*3,0,st-1);
if(st<n-1) Getd(0,n*3,st+1,n-1);
ll ans=max(a[min(n*2,D)],b[min(n*2,D)]);
for(int i=0;i<=n*2&&i<D-1;i++)
{
ans=max(ans,a[i]+c[D-i-1]);
ans=max(ans,b[i]+d[D-i-1]);
}
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  BZOJ 分治 主席树