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的值
轉移就是經典揹包