luogu P5892 [IOI2014]holiday 假期 决策单调性优化dp 主席树
LINK:holiday
考虑第一个subtask.
容易想到n^2暴力枚举之后再暴力计算答案.
第二个subtask 暴力枚举终点可以利用主席树快速统计答案.
第三个subtask 暴力枚举两端利用主席树计算贡献。
最后一个 subtask.
容易想到还是固定左端点来不断的寻找右端点。
不过很遗憾这最多只能做到\(n^2logn\)
需要从其他的角度入手 感觉前面几个subtask一直在迷惑选手。
可以从天数下手 左边多少天右边多少天。
显然 需要预处理出\(f1_i,f2_i\)分别表示从起点向右走i天能获得的最大价值,向右并且回到起点的最大价值.
左边同理.
答案利用这两个数字进行拼接即可。
值得注意的是 起点只能分给左右两边的一个并判断好边界问题。
然后就是求f数组了 容易想到枚举决策+贡献。
可以发现这是具有决策单调性的。证明不需要四边形不等式 下面我口胡一个。
假设有\(i,j,p\)其中\(p\)是\(j\)的最优决策 且\(i<j\) 那么对于i的最优决策一定\(<=p\)
反证法:若i的最优决策\(k>p\) 那么\(j\)也可以用\(k\)这个决策 i可以用\(p\)这个决策 把i在p这个决策时候用到的点和 在k用到的点标记。
可以发现k用到的点的和大于i用到的点的和 j也用k的话 由于j在p是包含i在p的选择 所以j也可以变成i \(p->k\)的样子 且其他东西不变 可以发现这样会更优。
所以j的最优决策会是k而不是p 与原命题相悖 所以假设成立。
总复杂度\(n\cdot log^2n\)
code
//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 10000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define gi(x) scanf("%lf",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define gc(a) scanf("%s",a+1)
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define fep(n,p,i) for(RE int i=n;i>=p;--i)
#define vep(p,n,i) for(RE int i=p;i<n;++i)
#define pii pair<int,int>
#define mk make_pair
#define RE register
#define P 1000000007
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define EPS 1e-4
#define sq sqrt
#define S second
#define F first
#define mod 1000000007
#define V vector<int>
#define l(x) t[x].l
#define r(x) t[x].r
#define sum(x) t[x].sum
#define cnt(x) t[x].cnt
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
RE int x=0,f=1;RE char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
const int MAXN=100010,maxn=300010;
int n,m,s1,op,ans,top,id;
int a[MAXN],b[MAXN],root[MAXN];
ll sum[MAXN];
ll f1[maxn];//从原点出发向右走的最大值.
ll f2[maxn];//从原点出发向右走之后走回起点的最大值.
ll f3[maxn];//从原点出发向左走的最大值.
ll f4[maxn];//从原点出发向左走之后走回起点的最大值.
struct wy
{
int l,r;
ll sum;
int cnt;
}t[MAXN*20];
inline void discrete()
{
sort(a+1,a+1+n);
rep(1,n,i)if(i==1||a[i]!=a[i-1])a[++top]=a[i];
rep(1,n,i)b[i]=lower_bound(a+1,a+1+top,b[i])-a;
}
inline void insert(int las,int &p,int l,int r,int x)
{
p=++id;t[p]=t[las];
sum(p)+=a[x];++cnt(p);
if(l==r)return;
int mid=(l+r)>>1;
if(x<=mid)insert(l(las),l(p),l,mid,x);
else insert(r(las),r(p),mid+1,r,x);
}
inline ll ask(int x,int y,int l,int r,int k)
{
if(l==r)return (ll)k*a[l];
int mid=(l+r)>>1;
if(cnt(r(x))-cnt(r(y))>=k)return ask(r(x),r(y),mid+1,r,k);
return ask(l(x),l(y),l,mid,k-cnt(r(x))+cnt(r(y)))+sum(r(x))-sum(r(y));
}
inline ll ask(int l,int r,int k)
{
//求区间l~r的前k大的和.
ans=0;
if(k>=r-l+1)return sum[r]-sum[l-1];
return ask(root[r],root[l-1],1,top,k);
}
inline void solve1(int l,int r,int L,int R)
{
int mid=(l+r)>>1;int p=L,res;
rep(L,R,j)
{
res=mid-op*(j-s1);
if(res<=0)continue;
ll cc=ask(s1,j,res);
if(cc>f1[mid])
{
f1[mid]=cc;
p=j;
}
}
if(l<mid)solve1(l,mid-1,L,p);
if(r>mid)solve1(mid+1,r,p,R);
}
inline void solve2(int l,int r,int L,int R)
{
int mid=(l+r)>>1;int p=L,res;
rep(L,R,j)
{
res=mid-op*(j-s1);
if(res<=0)continue;
ll cc=ask(s1,j,res);
if(cc>f2[mid])
{
f2[mid]=cc;
p=j;
}
}
if(l<mid)solve2(l,mid-1,L,p);
if(r>mid)solve2(mid+1,r,p,R);
}
inline void solve3(int l,int r,int L,int R)
{
int mid=(l+r)>>1;int p=L,res;
fep(L,R,j)
{
res=mid-op*(s1-j);
if(res<=0)continue;
ll cc=ask(j,s1-1,res);
if(cc>f3[mid])
{
f3[mid]=cc;
p=j;
}
}
if(l<mid)solve3(l,mid-1,L,p);
if(r>mid)solve3(mid+1,r,p,R);
}
inline void solve4(int l,int r,int L,int R)
{
int mid=(l+r)>>1;int p=L,res;
fep(L,R,j)
{
res=mid-op*(s1-j);
if(res<=0)continue;
ll cc=ask(j,s1-1,res);
if(cc>f4[mid])
{
f4[mid]=cc;
p=j;
}
}
if(l<mid)solve4(l,mid-1,L,p);
if(r>mid)solve4(mid+1,r,p,R);
}
int main()
{
//freopen("1.in","r",stdin);
get(n);get(s1)+1;get(m);
rep(1,n,i)b[i]=get(a[i]),sum[i]=sum[i-1]+a[i];
discrete();
rep(1,n,i)insert(root[i-1],root[i],1,top,b[i]);
op=1;solve1(1,m,s1,n);//规定右边带起点.
op=2;solve2(1,m,s1,n);
op=1;if(s1-1>=1)solve3(1,m,s1-1,1);
op=2;if(s1-1>=1)solve4(1,m,s1-1,1);
ll ans=0;rep(0,m,i)ans=max(ans,max(f1[i]+f4[m-i],f2[i]+f3[m-i]));
putl(ans);return 0;
}