24暑假集训day3下午

实现代码:

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;
}

posted @ 2024-08-03 17:18  Yantai_YZY  阅读(17)  评论(0)    收藏  举报