24暑假集訓day3下午

Yantai_YZY發表於2024-08-03

實現程式碼:

int exgcd(int a,int b, int &x, int &y){
	if(b == 0){
		x = 1;
		y = 0;
		return a;
	}
	int d = exgcd(b, a % b, x, y);
	int k = x;
	x = y;
	y = k - (a / b) * y;
	return d;
}
int main(){
	int a = read(), b = read();
	int x, y;
	int d = exgcd(a, b, x, y);
	cout << d << " " << x << " " << y << '\n';
	return 0;
}


#define ll long long 
ll fac[N], ifac[N];
const ll p=998244353;
ll power(ll a, ll b){
	ll ret = 1;
	while (b){
		if(b & 1){
			ret = ret * a % p;
		}
		a = a * a % p;
		b >>= 1;
	}
	return ret;
}
ll c(int a, int b){
	if(a < b || b < 0){
		return 0;
	}
	return fac[a] * ifac[b] % p * ifac[a - b]% p;
}
void initialize(){
	fac[0]= 1;
	for(int i=1; i < N; i++){
		fac[i] = fac[i - 1] * i % p;
	}
	ifac[N - 1] = power(fac[N - 1], p - 2);
	for(int i = N - 2;i >= 0;i--){
		ifac[i]= ifac[i + 1] * (i + 1) % p;
	}
}

小凱的數字

思路:

我們知道 \(𝑥≡𝑥\)的"數位和" \((\mod 9)\)

所以 \(\overline{𝑙(𝑙+1)(𝑙+2)\cdots r}\)\(≡𝑙\)的"數位和"\(+(𝑙+1)\)的"數位和"\(+⋯+𝑟\)的"數位和"\(≡𝑙+(𝑙+1)+…+𝑟 (\mod 9)\)

問題變為計算 \(\frac{(𝑙+𝑟)(𝑟−𝑙+1)}{2}\)\(/2\) 可以變為 \(×5\)

std:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#include<set>
#include<unordered_map>
#include<bitset>
#define int long long
using namespace std;
const int MAXN=100005;

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while (ch<'0'||ch>'9'){
		  if (ch=='-') f=-1;
		  ch=getchar();
	}
	while (ch>='0'&&ch<='9'){
		  x=x*10+ch-48;
		  ch=getchar();
	}
	return x*f;
}
signed main(){
	int q;
	long long ans;
	q=read();
	while(q--){
		int l,r;
		l=read();
		r=read();
		ans=(l+r)%9*(r-l+1)%9*5%9;
		cout<<ans<<'\n';
	}
	return 0;
}

中國剩餘定理

給定一些 \(x≡a_𝑖 \pmod {m_i}\) 的限制,求 \(x\)。保證 \(m_i\) 兩兩互質。
我們只需要每次合併兩個方程。

現在考慮

\[\begin{cases} x≡a_1 \pmod{ m_1} \\ x≡a_2 \pmod{ m_2} \end{cases} \]

\[\begin{cases} a_1 + m_1k_1 = x\\ a_2 + m_2k_2 = x \end{cases} \]

代入得

\[m_1k_1−m_2k_2=a_2−a_1 \]

。使用擴充套件歐幾里得演算法計算出 \(k_1,k_2\)(由於 \(\gcd⁡(m_1,m_2 )=1\),則一定有解),然後代回得到 \(x\) 的一個特解 \(x_0\)

將兩個方程合併為一個新的方程 \(x≡x_0 \pmod {m_1 m_2}\)

實際上 \(k_1=(a_2−a_1 ) m_1^(−1)\),其中 \(m_1^(−1)\) 表示 \(m_1\)\(\mod m_2\) 下的逆元。


小凱的疑惑

思路:

\(𝑀\) 無法被支付,則 \(𝑎𝑥+𝑏𝑦=𝑀\) 沒有自然數解。

假設 \(𝑎𝑥_0+𝑏𝑦_0=𝑀\) 是一組特解,如果$ 𝑥_0,𝑦_0$ 其中有負數(不妨設 \(𝑦_0<0\)),就需要讓 \(𝑥_0\) 幫他勻一點,勻到 \(≥0\) 為止。

具體地,設$ 𝑥_0=𝑘𝑏+𝑟$ (\(0≤𝑟<𝑏\)) ,那麼$ 𝑎𝑟+𝑏(𝑦_0+𝑘𝑎)=𝑀$ 是最極限的情況。因為 \(𝑥_0\) 這一邊也需要 ≥0 。

如果此時$ 𝑦_0+𝑘𝑎$ 仍然 \(<0\) ,就說明無解。可知 \(𝑦_0+𝑘𝑎=−1,𝑟=𝑏−1\)\(𝑀\) 最大為 $𝑎(𝑏−1)−𝑏=𝑎𝑏−𝑎−𝑏 $

質數篩

素數判斷的方法

  1. 既然第一種方法時間複雜度太大,那麼我們要考慮一種新的方式。首先給出這種方法的名字——埃式篩。聽上去是不是一個很高大上的名字,但其實原理很簡單,我們用一個陣列來存放n以內的所有質數,我們要用到一個布林陣列來標記n以內的所有不是質數的數(也要標記1,1既不是質數也不是合數)。再標記此質數所有的倍數(因為質數所有的倍數都不是質數)。最後輸出所有質數

程式碼:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
bool is[10005];
int prim[10005];
int cnt=0;
void Aprime(int n){
   is[1]=1;//1既不是質數也不是合數 
   for(int i=2;i<=n;i++){//遍歷2~n之間的所有整數 
   	if(is[i]==0){//如果沒有被標記過,說明是質數 
   		prim[++cnt]=i;//儲存質數 
   		for(int j=i*2;j<=n;j+=i){//標記所有此質數的倍數 
   			is[j]=1;
   		}
   	}
   }
}
int main(){
   int n;
   cin>>n;
   Aprime(n);//呼叫函式 
   for(int i=1;i<=cnt;i++){
   	cout<<prim[i]<<"\n";//輸出質數 
   }
   return 0;
}

素數判斷的方法2:

埃式篩的缺點在於有和數被重複標記了多次,所以還不是最優解,這裡奉上更快的辦法——尤拉篩。尤拉篩,也叫線性篩,是更快的判斷質數的方法。其思路在埃式篩的基礎上有所改進,讓質數庫裡的每個質數分別與迴圈中的i相乘,如果i取餘這個質數等於0,則直接跳出迴圈,避免重複標記,節省時間。

程式碼:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
bool is[10005];
int prim[10005];
int cnt=0;
void Aprime(int n){
	is[1]=1;//1既不是質數也不是合數 
	for(int i=2;i<=n;i++){
		if(is[i]==0){//如果沒有被標記過,說明是質數
			prim[++cnt]=i;//儲存質數
		}
		for(int j=1;j<=cnt&&i*prim[j]<=n;j++){//質數不超過n 
			is[i*prim[j]]=1;//用新數去乘每一個質數 
			if(i%prim[j]==0){
				break;//不重複標記,取餘等於0就跳出迴圈 
			}
		}
	}
}
int main(){
	int n;
	cin>>n;
	Aprime(n);//呼叫函式 
	for(int i=1;i<=cnt;i++){
		cout<<prim[i]<<"\n";//輸出質數 
	}
	return 0;
}

選數

思路:
由於本題資料較水,對每個子集和暴力試除就能過。

但如果資料不水,需要使用線性篩,並且要壓一下空間。

std:

#include<bits/stdc++.h>
using namespace std;
bool isprime(int a){
    for(int i=2;i*i<=a;i++)if(a%i==0)return 0;
    return 1;
}
long long ans,a[25],n,k;
void dfs(int m, int sum, int s){
    if(m == k){
        if(isprime(sum))ans++;
        return ;
    }
    for(int i = s; i<n;i++)dfs(m+1,sum+a[i],i+1);
    return ;
}

int main(){
    scanf("%d%d",&n,&k);
    for(int i = 0; i < n; i++)scanf("%d",&a[i]);
    dfs(0, 0, 0);
    printf("%d\n",ans);
    return 0;
}

選素數

思路:
假設一開始是 𝑥 ,操作一次變為 𝑚 ,操作兩次變為 𝑛 。

第一次操作選的 \(𝑝_1\) 需要滿足 \(𝑚−𝑝_1<𝑥≤𝑚\)。所以對於一個固定的 \(𝑚\) ,最小的可行 𝑥=\(\min_{p|n}\min_{n-p<m\le n}f(m)\) 。設 \(𝑓(𝑚)=𝑚−𝑝_𝑚𝑎𝑥+1\)

第二次操作選的 \(𝑝_2\) 需要滿足 \(𝑛−𝑝_2<𝑚≤𝑛\)。我們需要找一個 \(𝑓\) 最小的 \(𝑚\),即答案為\(\min_{p|n}\min_{n-p<m\le n}f(m)\)

程式碼

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#include<set>
#include<unordered_map>
#include <climits>

#define ll long long

using namespace std;

const int MAXN = 1000010;
const int N = 1000000;

int n;
bool b[MAXN];
int prime[MAXN], idx;
int f[MAXN];
int ans = INT_MAX;

inline int get(int x){
	if (!b[x]) return INT_MAX;
	return x - f[x] + 1;
}

int main(){
	scanf("%d", &n);
	for (int i = 2; i <= N; ++i){
		if (!b[i]) prime[++idx] = f[i] = i;
		for (int j = 1; j <= idx && (ll) i * prime[j] <= N; ++j){
			b[i * prime[j]] = true;
			f[i * prime[j]] = max(f[i], prime[j]);
			if (i % prime[j] == 0) break;
		}
	}
	for (int i = get(n); i <= n; ++i)
		ans = min(ans, get(i));
	if (ans == INT_MAX) puts("-1");
	else printf("%d\n", ans);
	return 0;
}


數列之異或

思路:

\(2𝑘⊕(2𝑘+1)=1\)

所以 \(𝑁\) 為奇數時

\(1⊕2⊕3⊕⋯⊕𝑁=1⊕(2⊕3)⊕⋯⊕(𝑁−1⊕𝑁)=1⊕1⊕⋯⊕1\)\(=\begin{cases} 0, & \frac{N-1}{2}為奇數\\ 1, & \frac{N-1}{2}為偶數\\ \end{cases}\)

\(𝑁\) 為偶數時,令計算 \(𝑁−1\) 的答案,再異或上 \(𝑁\)

程式碼

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#include<set>
#include<unordered_map>
#include<bitset>
#define int long long
using namespace std;
const int MAXN=100005;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while (ch<'0'||ch>'9'){
		  if (ch=='-') f=-1;
		  ch=getchar();
	}
	while (ch>='0'&&ch<='9'){
		  x=x*10+ch-48;
		  ch=getchar();
	}
	return x*f;
}
signed main(){
	int n;
	n=read();
	if(n%2!=0){
		if(((n-1)/2)%2!=0){
			cout<<0;
		}else{
			cout<<1;
		}
	}else{
		if(((n-1)/2)%2!=0){
			cout<<(0^n);
		}else{
			cout<<(1^n);
		}
	}
	return 0;
}