2024ICPC武漢邀請賽-G.Pack-數論分塊、整除運算相關的不等式

yoshinow2001發表於2024-05-07

link:https://codeforces.com/gym/105143
Group contests:https://codeforces.com/group/DWEH34LQgT/contest/521901
題意:有 \(n\)\(A\) 物品, \(m\)\(B\) 物品,兩種物品價值分別是 \(a,b\),若干件 \(A\) 和若干件 \(B\) 可以打包成一個商品,打包儘可能多的商品的情況下讓剩餘的 \(A,B\) 物品件數和最小,同時商品價值恰好是 \(k\)


雖然感覺是比較基礎的題…但是寫的時候並不是那麼熟練,一些細節的地方處理了一小會兒,感覺還是有必要進行一個覆盤。

題意翻譯過來就是求 $$n+m-\min(\lfloor\frac{n}{x}\rfloor,\lfloor\frac{m}{y}\rfloor)\times(x+y)$$
的最小值,\(n+m\) 固定,也就是最大化後面的部分

注意到 \(n,m\) 對稱,而且 \(\lfloor n/x\rfloor\) 只有 \(\Theta(\sqrt n)\) 種取值,強制讓某個數的塊小(例如 \(x\) ),就變成了如下問題:

  • 找到每個 \(\lfloor n/x\rfloor=v\) 的塊,強制讓 \(v=\lfloor n/x\rfloor \leq \lfloor m/y\rfloor\)
  • 對每個塊 \(v\),可以確定 \(x\) 的一個取值範圍。
  • 對不定方程 \(ax+by=k\) ,先求得一組 \(ax_0+by_0=\gcd(a,b)\equiv d\) 的特解,將特解乘上 \(k/d\) 得到右側是 \(k\) 的特解,寫出其通解為

\[\begin{aligned}x&=x_0+t\cdot \frac{b}{d}\\ y&=y_0-t\cdot \frac{a}{d}\end{aligned}\]

  • 要求滿足不等式:
    • 1、\(x\geq 0\)\(\lfloor n/x\rfloor=v\).
    • 2、\(y\geq 0\)
    • 3、\(v \leq \lfloor m/y\rfloor\).
  • 分別求解:
    • 1、除法分塊得到一個 \(x\) 的範圍 \([L,R]\),所以\(L\leq x_0+tb/d\leq R\),$$\lceil(L-x_0)\frac{d}{b} \rceil \leq t\leq \lfloor (R-x_0)\frac{d}{b}\rfloor$$
    • 2、\(y=y_0-t\cdot a/d\geq 0\)\(t\leq \lfloor \frac{y_0\cdot d}{a}\rfloor\).
    • 3、這裡我第一次寫的時候想的是,\(\lfloor m/y\rfloor\) 也是 \(\Theta(\sqrt m)\) 種取值,預處理出每一段 \(y\) 的範圍,雙指標掃描。但後面仔細想想,這裡的 \(y\) 肯定是可以直接求解的(見後文)。
  • 可以得到一個 \(t\) 的取值範圍 \(t\in [l_t,r_t]\)\(x+y=(x_0+y_0)\cdot \frac{k}{d} +\frac{t}{d}(b-a)\),很明顯關於 \(t\) 單調,在端點取值即可

除法分塊相關的不等式

  • \(\lfloor n/x\rfloor\) 只有 \(O(\sqrt n )\) 種取值,並且在 \(x\leq \sqrt n\) 時,\(x\) 每次變化都導致 \(\lfloor n/x\rfloor\) 不同;而在 \(x\geq \sqrt n\) 時,\(x\) 改變 \(1\) 至多導致 \(\lfloor n/x\rfloor\) 變化 \(1\).

一些相關的不等式:

  • 1、對於不等式 \(\lfloor n/x\rfloor\geq v\),有 \(n/x\geq \lfloor n/x\rfloor\geq v\),得到 \(x\leq \lfloor n/v\rfloor\),可以得到一個 \(x\) 的上界,同時代入 \(\lfloor n/v\rfloor\),會有 $$\lfloor \frac{n}{\lfloor n/v\rfloor}\rfloor\geq \lfloor\frac{n}{n/v}\rfloor=\lfloor v\rfloor=v$$同時 $$\lfloor\frac{n}{\lfloor n/v\rfloor +1}\rfloor>\lfloor \frac{n}{n/v}\rfloor=v$$
    因此 \(\lfloor n/x\rfloor\geq v\) 的解恰是 \(x\leq \lfloor n/v\rfloor\).

  • 2、對於不等式 \(\lfloor n/x\rfloor\leq v\),直接取 \(x\geq \lfloor n/v\rfloor\) 明顯是不對的,例如 \(n=10,v=2\),當 \(x\geq 4\)\(\lfloor 10/x\rfloor \leq 2\),而 \(\lfloor 10/2\rfloor=5\)。應該考慮 $$\frac{n}{x}-1<\lfloor \frac{n}{x}\rfloor \leq v$$
    得到 $$\begin{aligned}\frac{n}{x}<v+1\ \Leftrightarrow\ x>\frac{n}{v+1}\ \Leftrightarrow\ x\geq \lfloor\frac{n}{v+1}\rfloor +1\end{aligned}$$
    這是一個下界,同時 $$\lfloor \frac{n}{\lfloor n/(v+1)\rfloor}\rfloor\geq \lfloor \frac{n}{n/(v+1)}\rfloor=v+1$$
    因此對不等式 \(\lfloor n/x\rfloor \leq v\) 的解應該恰是 \(\lfloor \frac{n}{v+1}\rfloor +1\),這是比較不同的地方。

題目程式碼

(寫的還是雙指標的寫法)

#include<bits/stdc++.h>
#define endl '\n'
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const ll INF=2e18;
int exgcd(ll &x,ll &y,int a,int b){
    if(!b){
        x=1;y=0;
        return a;
    }
    int d=exgcd(x,y,b,a%b);
    ll tx=x,ty=y;
    x=ty;y=tx-(a/b)*ty;
    return d;
}
int work(int n,int m,int a,int b,int k){
    //adjust
    ll x0,y0,d=exgcd(x0,y0,a,b);
    x0=x0*k/d;y0=y0*k/d;
    ll tx=(x0%(b/d)+(b/d))%(b/d),dd=(x0-tx)/(b/d);
    x0=x0-dd*(b/d);y0=y0+dd*(a/d);
    //solve
    ll ans=0;
    vector<int> seg;
    for(int y=1,pos;y<=m;y=pos+1){
        pos=min(m,m/(m/y));
        seg.push_back(pos);
    }
    int j=0;
    for(int x=1,R;x<=n;x=R+1){
        int L=x;
        R=min(n,n/(n/x));
        int lt=ceil((double)(L-x0)/(b/d));
        int rt=floor((double)(R-x0)/(b/d));

        while(j+1<seg.size()&&(m/seg[j+1])>=(n/x))j++;
        if(m/seg[j]<(n/x))continue;

        lt=max(lt,(int)ceil((double)(y0-seg[j])*d/a));
        rt=min(rt,(int)floor((double)y0*d/a));
        
        if(lt>rt)continue;
        ans=max(ans,(ll)(n/x)*(x0+y0+(ll)(b-a)/d*lt));
        ans=max(ans,(ll)(n/x)*(x0+y0+(ll)(b-a)/d*rt));
    }
    return ans;
}
int main(){
    fastio;
    int tc;cin>>tc;
    while(tc--){
        int n,m,a,b,k;
        cin>>n>>m>>a>>b>>k;
        cout<<n+m-max(work(n,m,a,b,k),work(m,n,b,a,k))<<endl;
    }
    return 0;
}

相關文章