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 }
View Code

 

posted @ 2019-09-05 15:16  小布鞋  阅读(316)  评论(0编辑  收藏  举报