Loading docs/math/bezouts.md +67 −64 Original line number Diff line number Diff line Loading @@ -4,69 +4,72 @@ 其内容是: $x,y$ 的不定方程 $ax + by = c$ 有整数解的充要条件是 $\gcd(a, b)\mid c$ 。 即为如果 $a$ 与 $b$ 互质,那么一定存在两个整数 $x$ 与 $y$,使得 $ax+by=1$。 设 $a,b$ 是不全为零的整数,则存在整数 $x,y$,使得 $ax+by=\gcd(a,b)$。 ## 证明 还没有添加。 1.若任何一个等于$0$,则$\gcd(a,b)=a$.这时定理显然成立。 ## 应用 2.若$a,b$不等于 0. 由于 $\gcd(a,b)=\gcd(a,-b)$ 不妨设 $a,b$ 都大于零,$a>=b,\gcd(a,b)=d$。 对 $ax+by=d$,两边同时除以$d$,可得 $(a1)x+(b1)y=1$,其中 $(a1,b1)=1$。 转证 $(a1)x+(b1)y=1$。由带余除法: $a1=(q1)b+(r1)$,其中 $0=<r1<b1$ $b1=(q2)(r1)+(r2)$,其中 $0=<r2<r1$ $(r1)=(q3)(r2)+(r3)$,其中 $0=<r3<r2$ ..... $$(rn-3)=(qn-1)(rn-2)+(rn-1)$$ $$(rn-2)=(qn)(rn-1)+(rn)$$ $$(rn-1)=(qn+1)(rn)$$ [Codeforces Round #290 (Div. 2) D. Fox And Jumping](http://codeforces.com/contest/510/problem/D) 于是,有 给出 $n$ 张卡片,分别有 $l_i$ 和 $c_i$。在一条无限长的纸带上,你可以选择花 $c_i$ 的钱来购买卡片 $i$,从此以后可以向左或向右跳 $l_i$ 个单位。问你至少花多少元钱才能够跳到纸带上全部位置。若不行,输出 $-1$。 $$\gcd(a1,b1)=\gcd(b1,r1)=\gcd(r1,r2)=...=(rn-1,rn)=1$$ - 正解:裴蜀定理+动态规划 故 - 最优解:裴蜀定理+ Dijkstra $$(rn-2)=(xn)(rn-1)+1$$ 分析该问题,先考虑两个数的情况,发现想要跳到每一个格子上,必须使得这些数通过数次相加或相加得出的绝对值为 $1$,进而想到了裴蜀定理。 即 如果 $a$ 与 $b$ 互质,那么一定存在两个整数 $x$ 与 $y$,使得 $ax+by=1$。 $$1=(rn-2)-(xn)(rn-1)$$ 由倒数第三个式子 $(rn-1)=(rn-3)-(xn-1)(rn-2)$ 代入上式,得 $$1=[1+(xn)(xn-1)](rn-2)-(xn)(rn-3)$$ 然后用同样的办法用它上面的等式逐个地消去 $(rn-2),...(r1)$, 可证得 $1=(a1)x+(b1)y$ ## 应用 !!! Codeforces Round #290 (Div. 2) D. Fox And Jumping 给出$n$张卡片,分别有 $l_i$ 和 $c_i$。在一条无限长的纸带上,你可以选择花 $c_i$ 的钱来购买卡片 $i$,从此以后可以向左或向右跳 $l_i$ 个单位。问你至少花多少元钱才能够跳到纸带上全部位置。若不行,输出 $-1$。 分析该问题,先考虑两个数的情况,发现想要跳到每一个格子上,必须使得这些数通过数次相加或相加得出的绝对值为 1 ,进而想到了裴蜀定理。 可以推出:如果 $a$ 与 $b$ 互质,那么一定存在两个整数 $x$ 与 $y$,使得 $ax+by=1$。 由此得出了若选择的卡牌的数通过数次相加或相减得出的绝对值为$1$,那么这些数一定互质,此时可以考虑动态规划求解。 不过可以转移思想,因为这些数互质,即为$0$号节点开始,每走一步求$\gcd$(节点号,下一个节点),同时记录代价,就成为了从$0$通过不断$\gcd$最后变为$1$的最小代价。 由于:互质即为最大公因数为 $1$,$\gcd(0,x)=x$ 这两个定理,可以证明该算法的正确。选择优先队列优化 Dijkstra 求解。 不过还有个问题,即为需要记录是否已经买过一个卡片,开数组标记由于数据范围达到 $10^9$ 会超出内存限制,可以想到使用 `unordered_map` (比普通的 `map` 更快地访问各个元素,迭代效率较低)来标记。 另外,`__gcd` 是 OI 禁用的函数,仅供平时练习所用。 ### Code ```cpp #include<bits/stdc++.h> using namespace std; int n,t[300],k[300]; unordered_map<int,int>mp; priority_queue<pair<int,int>>q; void add(int g,int k) { if(mp.find(g)==mp.end()||mp[g]>k) { mp[g]=k; q.push({-k,g}); } } int main() { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&t[i]); for(int i=0;i<n;i++) scanf("%d",&k[i]); add(0,1); while(!q.empty()) { int u=q.top().second; int w=-q.top().first; q.pop(); if(mp[u]!=w) continue; if(u==1) return printf("%d\n",w-1),0; for(int i=0;i<n;i++) add(__gcd(u,t[i]),k[i]+w); } puts("-1"); return 0; } ``` 由于:互质即为最大公因数为 $1$,$\gcd(0,x)=x$ 这两个定理,可以证明该算法的正确。选择优先队列优化 $Dijkstra$ 求解。 不过还有个问题,即为需要记录是否已经买过一个卡片,开数组标记由于数据范围达到$10^9$会超出内存限制,可以想到使用 ``unordered_map`` (比普通的 ``map`` 更快地访问各个元素,迭代效率较低,详见 [STL-map](https://oi-wiki.org/ds/stl/map/)) Loading
docs/math/bezouts.md +67 −64 Original line number Diff line number Diff line Loading @@ -4,69 +4,72 @@ 其内容是: $x,y$ 的不定方程 $ax + by = c$ 有整数解的充要条件是 $\gcd(a, b)\mid c$ 。 即为如果 $a$ 与 $b$ 互质,那么一定存在两个整数 $x$ 与 $y$,使得 $ax+by=1$。 设 $a,b$ 是不全为零的整数,则存在整数 $x,y$,使得 $ax+by=\gcd(a,b)$。 ## 证明 还没有添加。 1.若任何一个等于$0$,则$\gcd(a,b)=a$.这时定理显然成立。 ## 应用 2.若$a,b$不等于 0. 由于 $\gcd(a,b)=\gcd(a,-b)$ 不妨设 $a,b$ 都大于零,$a>=b,\gcd(a,b)=d$。 对 $ax+by=d$,两边同时除以$d$,可得 $(a1)x+(b1)y=1$,其中 $(a1,b1)=1$。 转证 $(a1)x+(b1)y=1$。由带余除法: $a1=(q1)b+(r1)$,其中 $0=<r1<b1$ $b1=(q2)(r1)+(r2)$,其中 $0=<r2<r1$ $(r1)=(q3)(r2)+(r3)$,其中 $0=<r3<r2$ ..... $$(rn-3)=(qn-1)(rn-2)+(rn-1)$$ $$(rn-2)=(qn)(rn-1)+(rn)$$ $$(rn-1)=(qn+1)(rn)$$ [Codeforces Round #290 (Div. 2) D. Fox And Jumping](http://codeforces.com/contest/510/problem/D) 于是,有 给出 $n$ 张卡片,分别有 $l_i$ 和 $c_i$。在一条无限长的纸带上,你可以选择花 $c_i$ 的钱来购买卡片 $i$,从此以后可以向左或向右跳 $l_i$ 个单位。问你至少花多少元钱才能够跳到纸带上全部位置。若不行,输出 $-1$。 $$\gcd(a1,b1)=\gcd(b1,r1)=\gcd(r1,r2)=...=(rn-1,rn)=1$$ - 正解:裴蜀定理+动态规划 故 - 最优解:裴蜀定理+ Dijkstra $$(rn-2)=(xn)(rn-1)+1$$ 分析该问题,先考虑两个数的情况,发现想要跳到每一个格子上,必须使得这些数通过数次相加或相加得出的绝对值为 $1$,进而想到了裴蜀定理。 即 如果 $a$ 与 $b$ 互质,那么一定存在两个整数 $x$ 与 $y$,使得 $ax+by=1$。 $$1=(rn-2)-(xn)(rn-1)$$ 由倒数第三个式子 $(rn-1)=(rn-3)-(xn-1)(rn-2)$ 代入上式,得 $$1=[1+(xn)(xn-1)](rn-2)-(xn)(rn-3)$$ 然后用同样的办法用它上面的等式逐个地消去 $(rn-2),...(r1)$, 可证得 $1=(a1)x+(b1)y$ ## 应用 !!! Codeforces Round #290 (Div. 2) D. Fox And Jumping 给出$n$张卡片,分别有 $l_i$ 和 $c_i$。在一条无限长的纸带上,你可以选择花 $c_i$ 的钱来购买卡片 $i$,从此以后可以向左或向右跳 $l_i$ 个单位。问你至少花多少元钱才能够跳到纸带上全部位置。若不行,输出 $-1$。 分析该问题,先考虑两个数的情况,发现想要跳到每一个格子上,必须使得这些数通过数次相加或相加得出的绝对值为 1 ,进而想到了裴蜀定理。 可以推出:如果 $a$ 与 $b$ 互质,那么一定存在两个整数 $x$ 与 $y$,使得 $ax+by=1$。 由此得出了若选择的卡牌的数通过数次相加或相减得出的绝对值为$1$,那么这些数一定互质,此时可以考虑动态规划求解。 不过可以转移思想,因为这些数互质,即为$0$号节点开始,每走一步求$\gcd$(节点号,下一个节点),同时记录代价,就成为了从$0$通过不断$\gcd$最后变为$1$的最小代价。 由于:互质即为最大公因数为 $1$,$\gcd(0,x)=x$ 这两个定理,可以证明该算法的正确。选择优先队列优化 Dijkstra 求解。 不过还有个问题,即为需要记录是否已经买过一个卡片,开数组标记由于数据范围达到 $10^9$ 会超出内存限制,可以想到使用 `unordered_map` (比普通的 `map` 更快地访问各个元素,迭代效率较低)来标记。 另外,`__gcd` 是 OI 禁用的函数,仅供平时练习所用。 ### Code ```cpp #include<bits/stdc++.h> using namespace std; int n,t[300],k[300]; unordered_map<int,int>mp; priority_queue<pair<int,int>>q; void add(int g,int k) { if(mp.find(g)==mp.end()||mp[g]>k) { mp[g]=k; q.push({-k,g}); } } int main() { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&t[i]); for(int i=0;i<n;i++) scanf("%d",&k[i]); add(0,1); while(!q.empty()) { int u=q.top().second; int w=-q.top().first; q.pop(); if(mp[u]!=w) continue; if(u==1) return printf("%d\n",w-1),0; for(int i=0;i<n;i++) add(__gcd(u,t[i]),k[i]+w); } puts("-1"); return 0; } ``` 由于:互质即为最大公因数为 $1$,$\gcd(0,x)=x$ 这两个定理,可以证明该算法的正确。选择优先队列优化 $Dijkstra$ 求解。 不过还有个问题,即为需要记录是否已经买过一个卡片,开数组标记由于数据范围达到$10^9$会超出内存限制,可以想到使用 ``unordered_map`` (比普通的 ``map`` 更快地访问各个元素,迭代效率较低,详见 [STL-map](https://oi-wiki.org/ds/stl/map/))