裴蜀定理学习记录

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的值

转移就是经典背包

 

posted @ 2024-07-26 23:36  liyishui  阅读(2)  评论(0编辑  收藏  举报