BZOJ4367: [IOI2014]holiday假期
Description
健佳正在制定下个假期去台湾的游玩计划。在这个假期,健佳将会在城市之间奔波,并且参观这些城市的景点。
在台湾共有n个城市,它们全部位于一条高速公路上。这些城市连续地编号为0到n-1。对于城市i(0<i<n-1)而言,与其相邻的城市是i-1和i+1。但是对于城市 0,唯一与其相邻的是城市 1。而对于城市n-1,唯一与其相邻的是城市n-2。
每个城市都有若干景点。健佳有d天假期并且打算要参观尽量多的景点。健佳已经选择了假期开始要到访的第一个城市。在假期的每一天,健佳可以选择去一个相邻的城市,或者参观所在城市的所有景点,但是不能同时进行。即使健佳在同一个城市停留多次,他也不会去重复参观该城市的景点。请帮助健佳策划这个假期,以便能让他参观尽可能多的景点。
Input
第1行: n, start, d.
第2行: attraction[0], ..., attraction[n-1].
n: 城市数。
start: 起点城市的编号。
d: 假期的天数。
attraction: 长度为n的数组;attraction[i] 表示城市i的景点数目,其中0≤i≤n-1。
Output
输出一个整数表示健佳最多可以参观的景点数。
Sample Input
5 2 7
10 2 20 30 1
10 2 20 30 1
Sample Output
60
HINT
假 设健佳有 7 天假期,有 5 个城市(参见下表),而且他由城市 2 开始。在第一天,健佳参观城市2的 20 个景点。第二天,健佳由城市 2 去往城市 3。而在第三天,健佳参观城市 3 的30 个景点。接下来的3天,健佳由城市 3 前往城市 0。而在第 7 天,健佳参观城市0的 10 个景点。这样健佳参观的景点总数是20+30+10=60,这是他由城市 2 开始、在 7 天假期内最多能参观的景点数目。
Source
首先我们考虑这样一个问题,如果给定健佳访问的城市区间,如何计算他最多能参观的景点数量。
我们发现这个问题在可持久化线段树上二分一下就可以解决了。
那么我们重新考虑这个问题,我们发现随着右端点的递增,左端点也是递增的,这样的决策单调性让我们联想到NOI2009诗人小G的做法。
设calc(x,y)表示左端点为x比左端点为y劣的最小右端点r,这个可以很容易通过二分套之前的算法解决。
我们维护决策序列Q,当新的决策x加入时,如果calc(Q[r-1],Q[r])>=calc(Q[r],x),那么Q[r]就是没有用的决策,因为Q[r]永远不可能成为最优决策。
那么这个问题就在O(Nlog^2N)的时间里解决了。
#include<cstdio> #include<cctype> #include<queue> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; const int BufferSize=1<<16; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } typedef long long ll; const int maxn=100010; const int maxnode=2000010; int n,S,d,A[maxn],tmp[maxn]; ll sumv[maxnode]; int root[maxn],s[maxnode],ls[maxnode],rs[maxnode],ToT; void build(int& y,int x,int l,int r,int p) { s[y=++ToT]=s[x]+1;sumv[y]=sumv[x]+tmp[p];if(l==r) return; int mid=l+r>>1;ls[y]=ls[x];rs[y]=rs[x]; if(p<=mid) build(ls[y],ls[x],l,mid,p); else build(rs[y],rs[x],mid+1,r,p); } ll query(int y,int x,int l,int r,int k) { if(l==r) return (ll)min(s[y]-s[x],k)*tmp[l]; int mid=l+r>>1; if(s[rs[y]]-s[rs[x]]>=k) return query(rs[y],rs[x],mid+1,r,k); return query(ls[y],ls[x],l,mid,k-s[rs[y]]+s[rs[x]])+sumv[rs[y]]-sumv[rs[x]]; } ll solve(int l,int r) { int w=r-l+min(r-S,S-l); if(d<=w) return 0; return query(root[r],root[l-1],1,n,min(r-l+1,d-w)); } int calc(int x,int y) { int l=S,r=n+1; while(l<r) { int mid=l+r>>1; if(solve(x,mid)<=solve(y,mid)) r=mid; else l=mid+1; } return l; } int Q[maxn],g[maxn]; int main() { n=read();S=read()+1;d=read(); rep(i,1,n) A[i]=tmp[i]=read(); sort(tmp+1,tmp+n+1); rep(i,1,n) A[i]=lower_bound(tmp+1,tmp+n+1,A[i])-tmp; rep(i,1,n) build(root[i],root[i-1],1,n,A[i]); int l=1,r=0; rep(i,1,S) { while(l<r&&g[r-1]>=calc(Q[r],i)) r--; Q[++r]=i;if(l<r) g[r-1]=calc(Q[r-1],Q[r]); } ll ans=0; rep(i,S,n) { while(l<r&&solve(Q[l],i)<=solve(Q[l+1],i)) l++; ans=max(ans,solve(Q[l],i)); } printf("%lld\n",ans); return 0; }