2016_1_13(3)
点餐
(dinner.cpp/c/pas)
【时间】1s
【问题描述】
清儿今天请好朋友们吃饭,一共N个人坐在坐在圆桌旁。
吃饭的第一步当然是点餐了。服务员拿来了M份菜单。第i个人阅读菜单并点出自己喜欢的菜需要花费时间T[i]。
当一个人点完菜之后,就会把菜单传到他右手边的第一个人。
M份菜单是同时发出的,每个菜单只能同时被一个人阅读。
清儿希望知道如何分发菜单,才能让点餐的总时间花费最少呢?
【输入】
输入文件名为dinner.in
输入第一行是N和M,表示人数和菜单数
输入第二行,N个数,表示每个人点餐所需要的时间。
【输出】
输出文件名为dinner.out
输出一个整数表示点餐花费的最小时间。
【输入输出样例】
#1
3 2
1 5 10
10
#2
4 2
1 2 3 4
5
【数据说明】
对于20%的数据,有1≤n≤100
对于60%的数据,有1≤n≤10000
对于100%的数据,有1≤n≤50000,T[i]<=600
这道题直接说了吧。我的做法是二分然后n^2 check,当时也想到了用倍增来优化那一个n^2,但是我比较蠢
只想到了mlogn的做法。然后正解是每次二分到一个ans之后处理出每一个点能到达的最远的点。然后跑一次倍增。
然后枚举起点,就可以logn check了。
总复杂度是nlogn^2
1 #include <cstdio> 2 #include <iostream> 3 #include <cstdlib> 4 #include <iostream> 5 #include <cstring> 6 using namespace std; 7 const int N = 100003; 8 int n,m,M = 1,cnt = 0; 9 int a[2*N],l,r,maxna; 10 int ans = 2000000000; 11 int anc[2*N][30]; 12 int st[3*N]; 13 inline void prepare(int mid) { 14 int sum = 0; 15 int h = 0 ,t = 0; 16 for(int i = 1 ; i <= n+n ; ++i) { 17 st[++t] = i; 18 if(sum + a[i] <= mid) sum += a[i]; 19 else { 20 do { 21 anc[st[++h]][0] = i; 22 sum -= a[st[h]]; 23 }while(sum + a[i] > mid); 24 sum += a[i]; 25 } 26 } 27 while(h<t) { 28 h++; 29 anc[st[h]][0] = 2*n+1; 30 } 31 for(int j = 0 ; j <= cnt ; ++j) anc[2*n+1][j] = 2*n+1; 32 for(int j = 1 ; j <= cnt ; ++j) { 33 for(int i = 1 ; i <= 2*n ; ++i) { 34 anc[i][j] = anc[anc[i][j-1]][j-1]; 35 } 36 } 37 } 38 inline bool check(int mid) { 39 if(mid < maxna) return false; 40 prepare(mid); 41 for(int i = 1 ; i <= n ; ++i) { 42 int k = m; 43 int x = i; 44 for(int j = cnt ; j >= 0 ; --j) { 45 if((1<<j)<=k) { 46 k -= (1<<j); 47 x = anc[x][j]; 48 } 49 } 50 if(x>=i+n) return true; 51 } 52 return false; 53 } 54 int main() { 55 freopen("dinner.in","r",stdin); 56 freopen("dinner.out","w",stdout); 57 scanf("%d%d",&n,&m); 58 while(m>=M) { 59 M<<=1; 60 cnt++; 61 } 62 cnt--; 63 for(int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]),r+=a[i],maxna = max(maxna,a[i]),a[i+n] = a[i]; 64 do { 65 int mid = (l+r)>>1; 66 if(check(mid)) ans = min(ans,mid) , r = mid; 67 else l = mid + 1; 68 }while(l<r); 69 if(check(l)) ans = min(ans,l); 70 cout << ans << endl; 71 }