題解 砍樹
阿新 • • 發佈:2021-06-19
第一眼:二分!n這麼小是方便跑check的吧
冷靜後:我單調性呢
於是考慮暴力
發現n很小,check會比較快
注意到如果i不合法,則i的倍數均不合法,考慮使用埃氏篩優化然而還是TLE30pts
正解是個整除分塊:
原式等價於求最大的d滿足
變形得
\[\sum\lceil\frac{a_i}{d}\rceil \leqslant \lfloor \frac{k+\sum a_i}{d} \rfloor \]注意不到右邊可以扯上整除分塊
而d越大,左式越小,所以使右式值不變的d中最大的一定更優
那這裡r只有\(2\sqrt{k+\sum a_i}\)
同時還可以注意到從右向左列舉,可以在找到第一個合法d的時候跳出,那考慮從右向左跑整除分塊
令\(r\)為右端點, \(n=k+\sum a_i\) \[r^{\prime} = \lfloor \frac{n}{r} \rfloor \]
那上一個右端點
\[r_2 = \lfloor \frac{n}{\lfloor \frac{n}{r} \rfloor+1} \rfloor \]就可以枚舉了
p.s. 以後別開float,本來常數就小不了多少,再爆float了就得不償失了對我這題爆float了
float上限\(1.571081981×10^{20}\)
大約是int長度(字面意)的二倍
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define N 110 #define ll long long #define ld long double #define usd unsigned #define ull unsigned long long //#define int long long #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++) char buf[1<<21], *p1=buf, *p2=buf; inline ll read() { ll ans=0, f=1; char c=getchar(); while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();} while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();} return ans*f; } ll n, k; ll a[N], sum, ans, maxn; bool check(ll d) { //cout<<"check "<<d<<endl; ll c=(sum+k)/d, t=0; for (int i=1; i<=n; ++i) {t+=ceil(double(a[i])/d); if (t>c) return 0;} //cout<<t<<' '<<c<<endl; return 1; } signed main() { #ifdef DEBUG freopen("1.in", "r", stdin); #endif ll c; n=read(); k=read(); for (int i=1; i<=n; ++i) a[i]=read(), sum+=a[i], maxn=max(maxn, a[i]); c=sum+k; maxn+=k; #if 0 for (ll l=1,r; l<=maxn; l=r+1) { r=c/(c/l); check(r); } #else for (ll r=maxn,l; r; r=l-1) { //cout<<l<<' '<<r<<endl; l=c/((c/r)+1)+1; if (check(r)) {printf("%lld\n", r); return 0;} } #endif printf("%lld\n", ans); return 0; }