1. 程式人生 > >uoj#29. 【IOI2014】Holiday

uoj#29. 【IOI2014】Holiday

target 貪心 大數 day cmp 4 sum ret const rac

http://uoj.ac/problem/29

經過的點集一定是一個包含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"

uoj#29. 【IOI2014】Holiday