当时初步感觉是一个类似动归或者贪心的神题,然而由于本题已经给出顺序,贪心貌似并没有什么道理,所以放弃贪心。然后又由于这是一个环的问题,我想到了“合并石子”那种环转链的思路,然后就是一个O(n^2*m)的近似背包的打法,虽然没有去打,但应该可行吧……
然后我又发现这道题貌似可以二分答案来进行check,然后我们就需要去枚举每一次的起始点,并进行模拟,然后加了一个剪枝即如果当前点的前缀和大于当前check的值,说明我们已经在给第一个点第一份菜单时给了他第二份菜单,而这又是不可行的,否则我们也不会搜到这了。于是乎打了一个最坏O(log sumT*n^2)的暴力模拟,当时傻乎乎的期望60分然后结果才得了20分。
正解是的确是二分答案,只不过在check时用的是倍增,由于二分可以达到同样的效果且省事,我就用了二分去打。其实考试时想到了二分去优化,然而实现的时候打挂了,再加上如果二分复杂度就是O(log sum*n*m*log n)而题目也没有说m的范围,我只能默认m也小于等于50000然后……就放弃了80分……
额,这道题是这次考试翻车的主要原因,出题人连对正解有直接影响的m都没说,也真是醉了……
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 #include<map> 9 #include<vector> 10 #define N 50005 11 using namespace std; 12 int n,t[N],mt,sm,m; 13 int sum[N*2]; 14 int get(int li,int ri,int L) 15 { 16 int t=li; 17 while(li<=ri) 18 { 19 int mid=(li+ri)>>1; 20 if(sum[mid]-sum[t-1]<=L) li=mid+1; 21 else ri=mid-1; 22 } 23 return li-1; 24 } 25 bool check(int L) 26 { 27 for(int i=1;i<=n;i++) 28 { 29 if(sum[i]>L)break; 30 int js=1; 31 for(int j=i;j<=i+n-1;j++) 32 { 33 j=get(j,i+n-1,L); 34 if(j<i+n-1) js++; 35 if(js>m)break; 36 } 37 if(js<=m)return 1; 38 } 39 return 0; 40 } 41 int main() 42 { 43 scanf("%d%d",&n,&m); 44 for(int i=1;i<=n;i++) 45 { 46 scanf("%d",&t[i]); 47 if(mt<t[i])mt=t[i]; 48 } 49 for(int i=1;i<=n*2;i++) 50 { 51 int to=i; 52 if(i>n)to-=n; 53 sum[i]=t[to]+sum[i-1]; 54 } 55 int li=mt,ri=sum[n],ans; 56 while(li<=ri) 57 { 58 int mid=(li+ri)>>1; 59 if(check(mid)) 60 { 61 ans=mid; 62 ri=mid-1; 63 } 64 else li=mid+1; 65 } 66 printf("%d\n",ans); 67 return 0; 68 }
时间: 2024-10-10 14:23:48