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

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;
}

  

posted @ 2016-04-20 16:32  wzj_is_a_juruo  阅读(244)  评论(0编辑  收藏  举报