Excrt 與拉格朗日乘子法
最近學到的數學知識有一點多,需要整理整理
\(Excrt\)
應該是NOIp的基礎內容,但我現在還沒有掌握紮實,整理下來
給定n個同餘方程
\(\begin{cases}x \equiv r_1 \ \ mod \ \ m_1\\x \equiv r_2 \ \ mod \ \ m_2\\ \vdots \\x\equiv r_n \ \ mod \ \ m_n \end{cases}\)
假設我們解出了第一個方程,設其為ans,我們知道第一個方程通解為\(x = ans + k \times m_1 , k \in \mathbb{Z}\)
那我們現在的目標就是求第一個方程與第二個方程同時滿足的x
考慮帶入第一個方程的通解帶入第二個方程
\(ans+k\times m_ 1\equiv r_2 \pmod{m_2}\)
移項可得
\(k\times m_1\equiv r_2 +ans \pmod{m_2}\)
問題轉化為求解
\[k\times m_1 + y \times m_2 = r_2 - ans \]這樣算出來的\(k = c + \frac{m_2}{\gcd(m_1,m_2)}\times t,t\in\mathbb{Z}\)
將k帶入原方程 \(ans' = ans + m_1 \times k\)
連個方程合併後的模數\(M = \operatorname{lcm}(m_1,m_2)\)
於是我可以將該方程與下一個方程繼續合併。
若求解的過程中解出的\(\gcd(m_1,m_2)\)不能整除\(r_2 -ans\)則方程無解
#include<bits/stdc++.h> using namespace std; #define int long long #define INF 1ll<<30 const int p=1e5 + 5; template<typename _T> inline void read(_T &x) { x=0;char s=getchar();int f=1; while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();} while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();} x*=f; } int n; int ai[p] , bi[p]; inline int mul(int a,int b,int mod) { int res = 0; while(b) { if(b&1) res = (res + a) % mod; a = a + a; a%=mod; b>>=1; } return res; } inline int gcd(int a,int b) { if(b == 0) return a; else return gcd(b,a%b); } inline int exgcd(int a,int b,int &x,int &y) { if(b == 0) { x = 1 , y = 0; return a; } int g = exgcd(b,a%b,x,y); int t = x; x = y , y = (t - (a/b) * y); return g; } inline void excrt() { int ans = ai[1]; int M = bi[1]; int x,y; for(int i=2;i<=n;i++) { int b = ((ai[i] - ans)%bi[i] + bi[i]) %bi[i]; int g = exgcd(M,bi[i] , x, y);//這裡應該判一下無解 x = mul(x , b/g , bi[i]); ans += M * x; M *= bi[i] / g; ans = (ans + M ) % M; } cout<<(ans + M)%M; } signed main() { read(n); for(int i=1;i<=n;i++) { read(bi[i]); read(ai[i]); } excrt(); return 0 ; }
拉格朗日乘子法
偶然發現的黑科技,能夠解決多元函式求極值問題
在數學和OI方面都是有力的工具
內容
給定一個二元函式\(f(x,y)\)和限制條件\(\phi(x,y)\),要求在滿足\(\phi(x,y)=0\)的情況下,求\(f(x,y)的最值。\)
設一個三元函式\(F(x,y,\lambda) =f(x,y)+\lambda\times\phi(x,y)\)
對F求偏導,令每一個偏導數等於0,聯立求解方程即可
解出未知數再帶回原式即可
拉格朗日插值
建構函式
\[L_n(x) = \sum_{i=0}^{n}y_iℓ_i \]\[ℓ_i(x) = \prod_{i\neq j}^{n} \frac{x-x_j}{x_i-x_j} \]易知,對與已經給出的\(n+1\)個點對\((x_i,y_i)\),每個\(x_i\)在\(L_n(x)\)中唯一對應一個\(y_i\)
然後\(O(n^2)\)的時間內可以求出\(f(x)\)的值
當然可以更快
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 1ll<<30
template<typename _T>
inline void read(_T &x)
{
x=0;char s=getchar();int f=1;
while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
x*=f;
}
const int mod = 998244353;
const int np = 5e3 + 10;
int x[np] , y[np];
int n;
inline int power(int a,int b)
{
int res =1;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a;
a%=mod;
b>>=1;
}
return res;
}
inline int inv(int x)
{
return power(x , mod - 2);
}
inline void lagrange(int kth)
{
int ans =0 ;
for(int i=1;i<=n;i++)
{
int ell_up = y[i];//加在這
int ell_down =1 ;
for(int j=1;j<=n;j++)
{
if(i == j) continue;
ell_up = ell_up * (kth - x[j]) % mod;
ell_down = ell_down * (x[i] - x[j]) % mod;
}
ans = (ans + (ell_up * inv(ell_down) % mod)) % mod;//把y[i]加在這會乘爆,我就因為這個wa了一萬次
ans %=mod;
}
ans = (ans + mod) % mod;
printf("%lld",ans);
}
signed main()
{
int k;
read(n);read(k);
for(int i=1;i<=n;i++)
{
read(x[i]);
read(y[i]);
}
lagrange(k);
}
很明顯\(O(n^2)\)太慢了,但是考慮到NOIp不會專門考察多項式。大概只會出在一些陰間數學題裡面,需要暴力求出前幾項然後插值第 \(k\) 項。所以連續型插值對我們來說是必需掌握的
考慮對拉格朗日基函式進行優化
\[ℓ_i(x) = \prod_{i\neq j}^{n} \frac{x-x_j}{x_i-x_j} \]上式等價於
\[ℓ_i(x) = \prod_{i\neq j}^{n} \frac{x-j}{i-j} \]直接上結論
\[pre(i)=pre(i-1)\times(x-i) \]\[suf(i)=suf(i+1)\times(x-i) \]\[ℓ_i(x) = \frac{pre(i-1)\times suf(i+1)}{(i-1)!\times(-1)^{n-i}\times(n-i)!} \]額,我們就可以插值出 \(f(k)\)
CF622F The Sum of the k-th Powers
證明是\(k+2\)次多項式就略過了(反正是給我自己看,而且我會
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 1ll<<30
template<typename _T>
inline void read(_T &x)
{
x=0;char s=getchar();int f=1;
while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
x*=f;
}
const int mod = 1e9 + 7;
const int np = 1e6 + 5;
inline int power(int a,int b)
{
int res =1 ;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a;
a%=mod;
b >>= 1;
}
return res;
}
int suf[np] , pre[np] , fac[np];
inline int inv(int x)
{
return power(x , mod -2 );
}
signed main()
{
int n;
int k;
read(n);
read(k);
pre[0] = suf[k + 3] = fac[0] = 1;
for(int i=1;i<=k + 2;i++) pre[i] = pre[i - 1] * (n - i) % mod;
for(int i=k + 2;i>=1;i--) suf[i] = suf[i + 1] * (n - i) % mod;
for(int i=1;i<=k + 2;i++) fac[i] = fac[i-1] * i % mod;
int ans =0 , y= 0;
for(int i=1;i<=k + 2;i++)
{
y += power(i,k);
y%=mod;
int ell_up = pre[i - 1] * suf[i + 1] % mod;
int ell_down = fac[i-1] * fac[k + 2- i]%mod * (((k+ 2 - i) & 1)?-1:1)% mod;
(ans += (ell_up * y % mod * inv(ell_down) % mod)) %= mod;
}
printf("%lld",(ans + mod)%mod);
}
第二類斯特林數
\(\begin{Bmatrix}n\\m\end{Bmatrix}\)為第二類斯特林數,組合意義是:n個元素放到m個集合中的方案數
遞推公式:
\(\begin{Bmatrix}n\\m\end{Bmatrix} =m\begin{Bmatrix}n-1\\m\end{Bmatrix} +\begin{Bmatrix}n-1\\m-1\end{Bmatrix}\)
組合意義易證
通項公式:
\[\begin{Bmatrix}n\\m\end{Bmatrix} =\frac{1}{m!}\sum_{i=0}^{m}\dbinom{m}{i}(m-i)^n \]考慮容斥
m 個集合中有 i 個空置不用, n 個球在剩下的集合中隨意放置。