1. 程式人生 > 其它 >Excrt 與拉格朗日乘子法

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 個球在剩下的集合中隨意放置。