# CSES [Factory Machines](https://cses.fi/problemset/task/1620/) 題解 [TOC] ## 題目大意 給定$n$、$t$、$k_1,k_2,\dots,k_n$,求最小的$x$使得$\sum_{i=1}^{n}\lfloor \frac{x}{k_i}\rfloor\geq t$ - $1 \le n \le 2 \cdot 10^5$ - $1 \le t \le 10^9$ - $1 \le k_i \le 10^9$ ## 範例測資 input: ``` 3 7 3 2 5 ``` ouput: ``` 8 ``` ## 觀察 $x$越大,$\sum_{i=1}^{n}\lfloor \frac{x}{k_i}\rfloor$越大,反之亦然。 以範測為例: |$x$|1|2|3|4|5|6|7|8|9|10| |---|---|---|---|---|---|---|---|---|---|---| |$\sum_{i=1}^{n}\lfloor \frac{x}{k_i}\rfloor$|0|1|2|3|4|6|6|7|8|10| 因為我們只在乎它有沒有大於$t$,所以可以轉換如下: |$x$|1|2|3|4|5|6|7|8|9|10| |---|---|---|---|---|---|---|---|---|---|---| |是否大於等於$t$|0|0|0|0|0|0|0|1|1|1| 存在單調性,因此可以使用**二分搜**。 ## 實作 可以使用變數$l$作為左界,表示目前最大的**不可能為答案的數**;變數$r$為右界,表示目前最小的**可能為答案的數**。 每次檢查$\text{mid}=\frac{l+r}{2}$是否可能為答案,並更新左界或右界,直到$r=l+1$為止,此時的$r$就是答案。 ## code ```cpp= #include<bits/stdc++.h> #define ll long long //將程式碼中的ll替換為long long using namespace std; ll n,t; vector<ll> k; bool check(ll x){ //檢查x是否有可能是答案 ll temp=0; for(int i=0;i<n;i++){ temp+=x/k[i]; if(temp>=t) return 1; //為避免溢位,發現有可能是答案時就直接回傳 } return 0; } int main(){ cin>>n>>t; k.resize(n); for(int i=0;i<n;i++){ cin>>k[i]; } ll l=0,r=1e18,mid; //1e18=1*10^18 /*根據題目範圍發現,check(0)一定為0,check(1e18)一定為1, 因此選擇這兩個數作為一開始的左界和右界*/ while(r-l>1){ //做到r==l+1為止 mid=(l+r)/2; if(check(mid)) r=mid; //mid有可能是答案,更新右界。 else l=mid; //mid不可能是答案,更新左界 } cout<<r<<endl; return 0; } ``` ## 複雜度分析 每次把左、右界的距離減半,直到距離為1,因此共需檢查$\log_{2}(1e18)$個數字。 每次檢查數字最差都需要把整個$k$跑過一遍,要花$\text{O}(n)$的時間 總時間複雜度約為$\text{O}(n\log_{2}(1e18))$ :::info 若$a^b=c$,且$a,c>0,a\neq 1$,則$b=\log_{a} c$ :::
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.