BZOJ4367 IOI2014holiday假期(整体二分+主席树)
显然最优策略是先走到一边要到达的最远城市,再换方向走到另一边要到达的最远城市(当然也可以直接停止),路上参观景点。
先仅考虑求出只向左走,花费时间i时的最优解。如果能求出这个,类似的就可以求出所有情况。
显然时间越长,应该往左边走的越远,参观的越多,但是这个最远城市的变化不一定连续,没法愉快地双指针或者直接二分答案。
考虑类似整体二分的做法。设当前要求i在l~r,最远城市在x~y之间的最优解,对i=mid暴力求出最优解,然后递归处理两边。暴力需要进行nlog次,每次暴力需要求区间k大和,可以用主席树做到log,于是复杂度O(nlog2n)。
注意起点处只能取一次。并且对mid求出的最远点如果有多解考虑清楚选哪一个。
upd:才想起来这玩意就是决策单调性
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<cassert> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 100010 #define ll long long int n,start,m,a[N],b[N],root[N],cnt,t; ll fl[N<<2],gl[N<<2],fr[N<<2],gr[N<<2]; struct data{int l,r,x;ll sum; }tree[N*25]; struct data2{ll x;int d;}; void ins(int &k,int l,int r,int x) { tree[++cnt]=tree[k],k=cnt; tree[k].x++,tree[k].sum+=b[x]; if (l==r) return; int mid=l+r>>1; if (x<=mid) ins(tree[k].l,l,mid,x); else ins(tree[k].r,mid+1,r,x); } ll query(int x,int y,int l,int r,int k) { if (!y) return 0; if (l==r) return 1ll*min(tree[y].x-tree[x].x,k)*b[l]; int mid=l+r>>1,t=tree[tree[y].r].x-tree[tree[x].r].x; if (t>=k) return query(tree[x].r,tree[y].r,mid+1,r,k); else return tree[tree[y].r].sum-tree[tree[x].r].sum+query(tree[x].l,tree[y].l,l,mid,k-t); } data2 calc(int tot,int x,int y,int op,int isr) { int mx=isr?x:y;ll ans=0; if (isr) { for (int i=x;i<=y;i++) if (tot>=abs(start-i)*(1+op)) { ll v; if (i<start) v=query(root[i-1],root[start-op],1,t,tot-(start-i)*(1+op)); else v=query(root[start-1+op],root[i],1,t,tot-(i-start)*(1+op)); if (v>ans) ans=v,mx=i; } } else { for (int i=y;i>=x;i--) if (tot>=abs(start-i)*(1+op)) { ll v; if (i<start) v=query(root[i-1],root[start-op],1,t,tot-(start-i)*(1+op)); else v=query(root[start-1+op],root[i],1,t,tot-(i-start)*(1+op)); if (v>ans) ans=v,mx=i; } } return (data2){ans,mx}; } void solve1(int l,int r,int x,int y) { if (l>r||x>y) return; int mid=l+r>>1;data2 t=calc(mid,x,y,0,0); fl[mid]=t.x; solve1(l,mid-1,t.d,y); solve1(mid+1,r,x,t.d); } void solve2(int l,int r,int x,int y) { if (l>r||x>y) return; int mid=l+r>>1;data2 t=calc(mid,x,y,0,1); fr[mid]=t.x; solve2(l,mid-1,x,t.d); solve2(mid+1,r,t.d,y); } void solve3(int l,int r,int x,int y) { if (l>r||x>y) return; int mid=l+r>>1;data2 t=calc(mid,x,y,1,0); gl[mid]=t.x; solve3(l,mid-1,t.d,y); solve3(mid+1,r,x,t.d); } void solve4(int l,int r,int x,int y) { if (l>r||x>y) return; int mid=l+r>>1;data2 t=calc(mid,x,y,1,1); gr[mid]=t.x; solve4(l,mid-1,x,t.d); solve4(mid+1,r,t.d,y); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4367.in","r",stdin); freopen("bzoj4367.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),start=read()+1,m=read(); for (int i=1;i<=n;i++) b[i]=a[i]=read(); sort(b+1,b+n+1); t=unique(b+1,b+n+1)-b-1; for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+t+1,a[i])-b; for (int i=1;i<=n;i++) { root[i]=root[i-1]; ins(root[i],1,t,a[i]); } solve1(0,m,1,start); solve2(0,m,start,n); solve3(0,m,1,start-1); solve4(0,m,start+1,n); ll ans=0; for (int i=0;i<=m;i++) ans=max(ans,gl[i]+fr[m-i]), ans=max(ans,gr[i]+fl[m-i]); cout<<ans; return 0; }