2019南京icpc网络赛 I题 李超树
题意:有n个人,每个人都会有个到达时间 ti ,然后每个人手洗衣服的时间都是y,问你对于每一个机洗时间x(x属于1到y)的所有人最晚洗完衣服的时间。
题目链接:https://nanti.jisuanke.com/t/41306
题解:这道题当时1%的通过率。。。
首先,这些人当中,肯定是前面的都是手洗,然后到了某个人后,后面的人都是机洗。因为假如说 第 i 人手洗,然后i+1人机洗,i + 2手洗,那么i+1肯定也手洗,因为i+1手洗肯定不会对答案有影响。
然后我们可以得到一个这样的柿子,对于某个特定的x,答案为max( f(i) , g(i) ),其中f(i) 为 t[i-1] + y 。g(i) 为 max( t[j] + (n-j+1) *x) 其中j从i到 n。先解释一下柿子怎么来的。意思是枚举第i 个人开始机洗,那么此时最晚时间的可能是第 i - 1 的人手洗而来,或者是后面机洗而来。
解释一下g(i):举个栗子,假如只有一个人,那么显然成立,假如有两个人,倒数第二个人要么是机洗完,倒数第一个人还没来,这样子g(n-1)肯定比g(n)小,答案是g(n),所以柿子成立。假如倒数第二个人机洗完,倒数第一还没来,那答案就是g(n-1),类推下去。
然后我们发现,f(i)是递增的,g(i)是递减的(因为越少人肯定可能答案比越多人的少),所以柿子就是一个V形的,。然后x越小,g(i)肯定越小。所以我们可以从大到小枚举x,对于每一个x,从大到小枚举i,(也就是那个极小值点),然后x减小后,那个极小值肯定往左边移动,所以可以在上一次x枚举的i的基础上继续往左找。然后怎么快速的找对于一个特定的x的g(i)。g(i)其实就是若干条直线,x下标的最大值,这个用李超树维护,然后左移i就要加一条直线进李超树,更新x也得搞搞李超树。然后枚举i和x就是(n+y)的,李超树查找logn的,总复杂度(n+y)logn。
WA点:李超树初始化应该把第n条线放进去,而不是第0条线。
1 /************************************************************************* 2 > File Name: nanjingI.cpp 3 # File Name: nanjingI.cpp 4 # Author : xiaobuxie 5 # QQ : 760427180 6 # Email:760427180@qq.com 7 # Created Time: 2019年09月03日 星期二 19时04分45秒 8 ************************************************************************/ 9 10 #include<iostream> 11 #include<cstdio> 12 #include<map> 13 #include<cmath> 14 #include<cstring> 15 #include<set> 16 #include<queue> 17 #include<vector> 18 #include<algorithm> 19 using namespace std; 20 typedef long long ll; 21 #define inf 0x3f3f3f3f 22 #define pq priority_queue<int,vector<int>,greater<int> > 23 ll gcd(ll a,ll b){ 24 if(a<b) return gcd(b,a); 25 return b==0?a:gcd(b,a%b); 26 } 27 28 const int N=1e6+9; 29 const int mx=1e6+9; 30 int p,nowx,cnt=0,n,y; 31 ll t[N],ans[N]; 32 int tr[mx*4]; 33 struct line{ 34 ll k,b; 35 }a[N]; 36 ll fun(int id,int x){ 37 return a[id].k * x + a[id].b; 38 } 39 void change(int o,int l,int r,int id){ 40 if(l==r){ 41 if( fun(id,l) > fun(tr[o],l) ) tr[o]=id; 42 return; 43 } 44 int m=(l+r)>>1; 45 if( a[tr[o]].k < a[id].k ){ 46 if( fun(id,m) > fun(tr[o],m) ){ 47 change(o<<1,l,m,tr[o]); 48 tr[o]=id; 49 } 50 else change(o<<1|1,m+1,r,id); 51 } 52 if( a[tr[o]].k > a[id].k ){ 53 if( fun(id,m) > fun(tr[o],m) ){ 54 change(o<<1|1,m+1,r,tr[o]); 55 tr[o]=id; 56 } 57 else change(o<<1,l,m,id); 58 } 59 } 60 ll query(int o,int l,int r,int x){ 61 if(l==r) return fun(tr[o],x); 62 int m=(l+r)>>1; 63 if(x<=m) return max( fun(tr[o],x), query(o<<1,l,m,x)); 64 else return max( fun(tr[o],x), query(o<<1|1,m+1,r,x)); 65 } 66 ll f(int i){return t[i-1]+y;} 67 ll g(int i){ 68 return query(1,0,y,nowx); 69 } 70 void build(int o,int l,int r){ 71 tr[o]=n; 72 if(l==r) return; 73 int m=(l+r)>>1; 74 build(o<<1,l,m); 75 build(o<<1|1,m+1,r); 76 } 77 int main(){ 78 while(~scanf("%d %d",&n,&y)){ 79 t[0]=-y; 80 memset(ans,0,sizeof(ans)); 81 build(1,0,y); 82 p=n; 83 for(int i=1;i<=n;++i) scanf("%lld",t+i); 84 sort(t+1,t+1+n); 85 for(int i=0;i<=n;++i) a[i]=(line){n-i+1,t[i]}; 86 for(nowx=y;nowx>=1;--nowx){ 87 ll cur=max(f(p),g(p)); 88 ll res=g(p); 89 while( p>=2 && max(f(p-1),max(res,fun(p-1,nowx))) <= cur){ 90 cur=max(f(p-1),max(res,fun(p-1,nowx))); 91 --p; 92 change(1,0,y,p); 93 res=max(res,fun(p,nowx)); 94 } 95 ans[nowx]=cur; 96 } 97 98 for(int i=1;i<=y;++i) printf("%lld%c",ans[i],i==y?'\n':' '); 99 } 100 return 0; 101 }