经过的点集一定是一个包含start的区间,为了经过这个区间内所有点,必须先到达一个区间端点,再到达另一个区间端点,剩余的步数则贪心选区间内最大价值的点。显然决策单调,于是可以分治,用可持久化线段树快速求出区间前k大数之和。
#include"holiday.h" #include<cstring> #include<algorithm> typedef long long i64; const int N=500007; i64 ans=0; int v[N],*vs[N],S,D; struct node{ node*c[2]; int sz; i64 s; }ns[N*21],*np=ns,*rt[N]; i64 kmx(int L,int R,int x){ node*w1=rt[L],*w2=rt[R+1]; i64 sum=0; for(int i=16;i>=0;--i){ int s=w1->c[0]->sz-w2->c[0]->sz; if(s<=x){ x-=s; sum+=w1->c[0]->s-w2->c[0]->s; w1=w1->c[1]; w2=w2->c[1]; }else{ w1=w1->c[0]; w2=w2->c[0]; } } return sum; } node*ins(node*w,int x,int v){ node*u=++np,*u0=u; for(int i=16;i>=0;--i){ int d=x>>i&1; u->c[d^1]=w->c[d^1]; u=u->c[d]=++np;w=w->c[d]; u->sz=w->sz+1; u->s=w->s+v; } return u0; } void calc1(int L,int R,int l,int r){ if(L>R)return; int M=L+R>>1,m=l,res=D-(S-M)*2-(l-S); i64 mv=-1; for(int i=l;i<=r&&res>=0;++i,--res){ i64 v=kmx(M,i,res); if(v>mv)mv=v,m=i; } if(ans<mv)ans=mv; calc1(L,M-1,l,m); calc1(M+1,R,m,r); } void calc2(int L,int R,int l,int r){ if(L>R)return; int M=L+R>>1,m=r,res=D-(M-S)*2-(S-r); i64 mv=-1; for(int i=r;i>=l&&res>=0;--i,--res){ i64 v=kmx(i,M,res); if(v>mv)mv=v,m=i; } if(ans<mv)ans=mv; calc2(L,M-1,l,m); calc2(M+1,R,m,r); } bool cmp(int*a,int*b){ return *a>*b; } i64 findMaxAttraction(int n,int start,int d,int attr[]){ rt[n]=ns->c[0]=ns->c[1]=ns; memcpy(v,attr,sizeof(int)*n); for(int i=0;i<n;++i)vs[i]=v+i; std::sort(vs,vs+n,cmp); for(int i=0;i<n;++i)*vs[i]=i; for(int i=n-1;i>=0;--i)rt[i]=ins(rt[i+1],v[i],attr[i]); D=d;S=start; calc1(std::max(0,S-d/2),S,S,n-1); calc2(S,std::min(n-1,S+d/2),0,S); return ans; } #include"grader.cpp"