裴蜀定理學習記錄

liyishui發表於2024-07-26

1477A - Nezzar and Board

觀察到2x-y可以拆成x+(x-y),現在模擬一下這個過程

發現得到的數可以看成從某個點xj出發,加上若干個兩數之間的差的形式。

再考慮一下2x-y的幾何意義,發現相當於在數軸上做x關於y的對稱點,並且和數的分佈位置有關,和具體數值是無關的

接下來有一個不太好想的key point:

設S為無限次操作後能得到的數的集合

如果存在0 ∈ S,那麼如果x ∈ S,則 nx ∈ S .........①

又因為可以平移,我們先讓所有ai和k一起減去a[1](這裡a[1]為a中其他值都一樣,只是為了湊出0)

則得到 a1-a1( =0 ),a2-a1,a3-a1...... an-a1,令新得到的陣列為a'。

現在問題變成從0出發,每次可以加任意兩數的差的絕對值,問是否能得到k-a1。

如果直接暴力處理出所有兩數的差,數量是O(n^2)的

但是發現做完差分( a2'-a1',a3'-a2',a4'-a3',a5'-a4' ...)之後,任意兩數的差ai'-aj'都可以由(ai'-ai-1')+(ai-1'-ai-2')+...(aj+1'-aj')得到

中間那部分可以消掉,寫成 ai'+(-ai-1'+ai-1')+(-ai-2'+ai-2')...(-aj+1'+aj+1')-aj'=ai'-aj'

這是i>j的情況,而i<j的情況可以直接乘個-1,依據是①

也就是我們用O(n)級別的兩數差表達了O(n^2)的情況,根據裴蜀定理,問題轉化成 k-a1 | gcd( a2'-a1',a3'-a2',a4'-a3',a5'-a4' ...)能否成立

注:裴蜀定理指的是ax+by=k若有解要求k | gcd(a,b),可以推廣到任意多項,證明見:裴蜀定理 - OI Wiki (oi-wiki.org)

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N = 2e5+5;
int a[N];
void solve(){
    int n,k;cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    k-=a[1];
    for(int i=2;i<=n;i++) a[i]-=a[1];
    a[1]-=a[1];
    int g=0;
    for(int i=2;i<=n;i++){
        g=__gcd(g,abs(a[i]-a[i-1]));
    }
    if(k%g==0){
        cout<<"YES"<<"\n";
    }
    else cout<<"NO"<<"\n";
}
signed main(){
    int t;cin>>t;
    while(t--){
        //TODO
        solve();
    }
}

510D - Fox And Jumping

問題轉化為挑出若干個li使得它們的值為gcd(根據裴蜀定理,它們可以線性組合出所有整數),且 Σci 最小

經典揹包,dp[i][j] 表示當前考慮到第i個物品,選出來的 li 的gcd值為mp[j]的最小代價

n<=300,所以它們的gcd組合數目不會很多,但又可能很大,解決辦法是開一個unordered_map儲存,mp[i]表示第i個gcd的值

轉移就是經典揹包