中国剩余定理模板+习题

除数互质

void extgcd(int a,int b,int &x,int &y)
{
    if(!b){x = 1,y = 0;}
    else{extgcd(b,a%b,y,x); y -= (a/b)*x;}
}
int inv(int a,int n)    
{
    int x,y;
    extgcd(a,n,x,y);           
    x = (x % n + n) % n;
    return x;
}
int china()  
{
    int p = 1,ans = 0;
    for(int i=0;i<n;i++)    p *= m[i];
    for(int i=0;i<n;i++){
        int w = p / m[i];
        ans = (ans + a[i] * w * inv(w,m[i])) % p;         
    }
    return ans;
}

除数不互质

void extgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(!b){d = a,x = 1,y = 0;}
	else{extgcd(b,a%b,d,y,x); y -= x*(a/b);}
}
ll China(){
	ll a1 = a[1],  n1 = n[1];   
	for(int i=2;i<=num;i++){
		ll a2 = a[i],	n2 = n[i];
		ll c = a2 - a1;
		ll k1,k2,d;
		extgcd(n1,n2,d,k1,k2);    //求解n1k1 - n2k2 = a2 - a1
		if(c % d)		return -1;
		c /= d,		n2 /= d;
		k1 *= c;
		k1 = (k1 % n2 + n2) % n2;  //找出最小的整数解
		a1 += n1 * k1;
		n1 *= n2;
	}
	return a1;
}
/*防止溢出*/
__int128 China(){
	__int128 a1 = a[1],  n1 = n[1];   
	for(int i=2;i<=num;i++){
		__int128 a2 = a[i],	n2 = n[i];
		__int128 c = a2 - a1;
		__int128 k1,k2,d;
		extgcd(n1,n2,d,k1,k2);
		if(c % d)		return -1;
		k1 = ((k1 * c / d) % (n2 / d) + (n2 / d)) % (n2 / d);
		a1 += n1 * k1;
		n1 = n1 * n2 / d;
	}
	return a1;
}

HDU3579

题解:输出最小正整数

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 20;
ll a[N],n[N];
int num;
void extgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(!b){d = a,x = 1,y = 0;}
	else{extgcd(b,a%b,d,y,x); y -= x*(a/b);}
}
ll China(){
	ll a1 = a[1],  n1 = n[1];   
	for(int i=2;i<=num;i++){
		ll a2 = a[i],	n2 = n[i];
		ll c = a2 - a1;
		ll k1,k2,d;
		extgcd(n1,n2,d,k1,k2);    //求解n1k1 - n2k2 = a2 - a1
		if(c % d)		return -1;
		c /= d,		n2 /= d;
		k1 *= c;
		k1 = (k1 % n2 + n2) % n2;  //找出最小的整数解
		a1 += n1 * k1;
		n1 *= n2;
	}
	return a1 > 0 ? a1 : n1;  //输出最小整数解,如果a1 = 0,那么a1 % n1还为0,所以a1 + n1.
}
int main(){
	int T,caser = 0;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&num);
		for(int i=1;i<=num;i++)	scanf("%lld",&n[i]);
		for(int i=1;i<=num;i++)	scanf("%lld",&a[i]);   
		printf("Case %d: %lld\n",++caser,China());
	}
	return 0;
}

HDU1573

题解:输出≤n的个数

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 10 + 5;
ll a[N],n[N],high;
int num;
void extgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(!b){d = a,	x = 1,	y = 0;}
	else{extgcd(b,a%b,d,y,x);	y -= x*(a/b);}
}
ll solve(){
	ll a1 = a[1],	n1 = n[1];
	for(int i=2;i<=num;i++){
		ll a2 = a[i],	n2 = n[i],	c = a2 - a1;
		ll k1,k2,d;
		extgcd(n1,n2,d,k1,k2);
		if(c % d)	return 0;
		n2 /= d,	c /= d;
		k1 *= c;
		k1 = (k1 % n2 + n2) % n2;
		a1 += k1 * n1;
		n1 *= n2;
	}
	if(a1 > high)	return 0;
	else if(a1 == 0)	return high / n1;    //必须要正整数
	else return (high - a1) / n1 + 1;
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%lld%d",&high,&num);
		for(int i=1;i<=num;i++)	scanf("%lld",&n[i]);
		for(int i=1;i<=num;i++)	scanf("%lld",&a[i]);
		printf("%lld\n",solve());
	}
	return 0;
}

POJ2891

题解:模板题
代码:

#include <algorithm>
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
typedef long long ll;
ll a1,a2,n1,n2;
int num;
void extgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(!b){d = a,x = 1,y = 0;}
	else{extgcd(b,a%b,d,y,x);	y -= a/b*x;}
}
int main(){
	while(~scanf("%d",&num)){
		bool flag = true;
		scanf("%lld%lld",&n1,&a1);
		for(int i=1;i<num;i++){
			scanf("%lld%lld",&n2,&a2);
			ll c = a2 - a1,k1,k2,d;
			extgcd(n1,n2,d,k1,k2);
			if(c % d)	flag = 0;
			n2 /= d,	c /= d;
			k1 *= c;
			k1 = (k1 % n2 + n2) % n2;
			a1 += n1 * k1;
			n1 *= n2;
		}
		printf("%lld\n",flag ? a1 : -1);	
	}
	return 0;
}

HDU1370

题解:还是模板题目,转换一下即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll const N =  21252;
ll a[5],n[5],m,d;
void extgcd(ll a,ll b,ll &x,ll &y){
	if(!b){x = 1,y = 0;}
	else{extgcd(b,a%b,y,x);	y -= x*(a/b);}
}
ll inv(ll a,ll n){
	ll x,y;
	extgcd(a,n,x,y);
	return (x % n + n) % n;
}
ll solve(){
	ll ans = 0;
	for(int i=1;i<=3;i++){
		ll w = m / n[i];
		ans = (ans + a[i] * w * inv(w,n[i])) % m;
	}
	return ans;
}
int main(){
	n[1] = 23,	n[2] = 28,	n[3] = 33;
	m = n[1] * n[2] * n[3];
	int caser = 0,T;
	scanf("%d",&T);
	while(cin>>a[1]>>a[2]>>a[3]>>d && d != -1){
		ll period = solve();
		while(period <= d)	period += m;   //第一次重合可能≤起始日期,下一个triple day,所以即使这一次是也不可以。
		printf("Case %d: the next triple peak occurs in %lld days.\n",++caser,period - d);
	}
	return 0;
}

HDU5668

题解:模拟一下。加入总共有n个人,一共数到K,现在统计的是第i个出局的人,从上一个出出局的人到这一个统计的个数为p。那么K % (n - i + 1) = p。每次出局一个做标记。这是一组除数为n - i + 1,余数为p的线性方程组。套模板。详情见代码。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 25;
int num[N],n,tmp,vis[N];
ll m[N],a[N];
void extgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(!b){d = a,x = 1, y = 0;}
	else{extgcd(b,a % b,d,y,x); y -= x * (a/b);}
}
ll solve(){
	ll a1 = a[1],	n1 = m[1];
	for(int i=2;i<=n;i++){
		ll a2 = a[i],	n2 = m[i],	c = a2 - a1;
		ll d,k1,k2;
		extgcd(n1,n2,d,k1,k2);
		if(c % d)	return -1;
		n2 /= d,	c /= d;
		k1 *= c;
		k1 = (k1 % n2 + n2) % n2;
		a1 += k1 * n1;
		n1 *= n2;
	}
	return a1;
}
int main(){
	int T;
	cin>>T;
	while(T--){
		cin>>n;
		memset(vis,true,sizeof(vis));   //表明在的人
		for(int i=1;i<=n;i++){
			cin>>tmp;   
			num[tmp] = i;   //第tmp个出局的是第i个人
		}
		int j = 1;    //现在是数到第j个人
		for(int i=1;i<=n;i++){	  //第i个出局的人
			int k = 1;  //统计余数
			while(num[i] != j){
				if(vis[j])	k++;
				j = j % n + 1;	 
			}
			vis[num[i]] = false;
 			m[i] = (ll)(n - i + 1);
			a[i] = (ll)k;
		}
		ll ans = solve();
		if(ans == -1)	printf("Creation August is a SB!\n");
		else 	printf("%lld\n",ans);
	}
}

HDU1930

题解:耐心看完题目,发现很简单。输出注意一下,最后的空格不能输出。否则PE。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 50 + 10;
int n;
char mp[N];
ll m[5],a[5],ans[N];
ll num;
void extgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(!b){d = a,	x = 1,	y = 0;}
	else{extgcd(b,a%b,d,y,x);	y -= x*(a/b);}
}
ll China(){
	ll a1 = a[1],	n1 = m[1];
	for(int i=2;i<=4;i++){
		ll a2 = a[i],	n2 = m[i],	c = a2 - a1;
		ll k1,k2,d;
		extgcd(n1,n2,d,k1,k2);
		if(c % d)	return 0;
		n2 /= d,	c /= d;
		k1 *= c;
		k1 = (k1 % n2 + n2) % n2;
		a1 += k1 * n1;
		n1 *= n2;
	}
	return a1;
}
int main(){
	int T;
	for(int i=1;i<=26;i++)	mp[i] = i + 'A' - 1;
	mp[27] = ' ';
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=4;i++)	cin>>m[i];
		for(int i=1;i<=n;i++){
			cin>>num;
			for(int k=4;k>=1;k--){
				a[k] = num % 100;
				num /= 100;
			}
			ans[i] = China();
		}
		for(int i=1;i<=n;i++){	//080527
			string s;
			s[2] = mp[ans[i] % 100],	ans[i] /= 100;
			s[1] = mp[ans[i] % 100],	ans[i] /= 100;
			s[0] = mp[ans[i]];
			if(i != n)	cout<<s[0]<<s[1]<<s[2];
			else{
				if(s[0] != ' ')	cout<<s[0];
				if(s[1] != ' ')	cout<<s[1];
				if(s[2] != ' ')	cout<<s[2];
			}
		}
		cout<<endl;
	}
	return 0;
}

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值