题目大意
可以理解成有n个木板,可以选取木板将其劈成2半(如果长度是奇数,就切成x和x+1),切完之后还可以再切
然后你要把这n个木板切成更多的木板,然后从中选择k个,使得这k个木板的最小长度尽量大
这个题有两种做法,不过都需要二分答案
先二分最小长度是x
第一种做法是 枚举n个木板,每一个都切到不能再切为止,然后统计有多少个木板,看能否符合
统计过程中要记录两个值,因为一个木板不论切多少次,结果都只会存在两种木板,然后记录一下每次切是哪两种木板以及各有多少个,然后简单转移即可
复杂度是nlog^2c
第二种做法是 类似dp的做法,dp[i]表示长度为i的木板有多少个,那么转移很简单,如果(i+1)/2仍不小于x,那么可以转移到dp[(i+1)/2]和dp[i/2]
最后统计i大于x的dp[i]的值即可
复杂度是clogc
一开始写的第一种做法,常数写渣了,TLE,好气啊orz
还是第二种做法比较神奇
#include <iostream> #include <cstring> #include <cstdio> #include <vector> using namespace std; typedef long long LL; int n, k; int a[1000001]; LL dp[10000002]; bool can(int x) { memset(dp, 0, sizeof(dp)); for(int i = 0; i < n; i++) dp[a[i]]++; LL ans = 0; for(int i = 10000001; i >= max(x, 2); i--) { if(dp[i]) { if((i+1)/2 >= x) { dp[i/2] += dp[i]; dp[(i+1)/2] += dp[i]; } else ans += dp[i]; } } if(x == 1) ans += dp[1]; return ans >= k; } int main() { cin>>n>>k; for(int i = 0; i < n; i++) scanf("%d", &a[i]); int l = 1, r = 1e7; while(l+1 < r) { int mid = (l+r)>>1; if(can(mid)) l = mid; else r = mid; } if(!can(1)) cout<<"-1"<<endl; else { if(can(l+1)) cout<<l+1<<endl; else cout<<l<<endl; } }