poj 1006

题目概述:

某数除以23余a,除以28余b,除以33余c,这个数减去d后的数大于0且小于等于21252

输入:

每行a,b,c,d,多组数据之间没有空行,输入以一行-1 -1 -1 -1结束

限制:

0<=a,b,c,d<=365

输出:

每行一个字符串,若用%times%表示第%times%组(从1开始计数)数据,%num%表示这个数减去d后且在要求范围内的数,则字符串为

Case %times%: the next triple peak occurs in %num% days.

注意即便%num%==1,输出也是的也是days

样例输入:

0 0 0 0

0 0 0 100

5 20 34 325

4 5 6 7

283 102 23 320

203 301 203 40

24 29 34 0

24 29 34 1

24 29 34 2

-1 -1 -1 -1

样例输出:

Case 1: the next triple peak occurs in 21252 days.

Case 2: the next triple peak occurs in 21152 days.

Case 3: the next triple peak occurs in 19575 days.

Case 4: the next triple peak occurs in 16994 days.

Case 5: the next triple peak occurs in 8910 days.

Case 6: the next triple peak occurs in 10789 days.

Case 7: the next triple peak occurs in 1 days.

Case 8: the next triple peak occurs in 21252 days.

Case 9: the next triple peak occurs in 21251 days.

讨论:

这个题里有大量的常数,本来可以写的更短,但是为了方便看就没写更短

中国剩余定理的应用,不得不说着实有些许理解上的难度,额到最后也没理解,不过倒是把实现都背过了(估计迟早要背错),对于三个数(不是三个呢?)的实现,流程大概是:

在三个除数中依次选一个,从另外两个除数的公倍数中找出可被这个除数除后余1的最小正数,用这个正数乘以选择的数对应的余数,完成三次后把三个结果加和,并对三个数的最小公倍数求模,这就是结果

用字母表示,有除数d1,d2,d3,及其最小公倍数lcm和余数r1,r2,r3,选择d1,令[(d2*d3)*n-1]/d1=m,化简后为(d2*d3)*n-d1*m=1,这里需要的正整数是(d2*d3)*n,之后得到(d2*d3)*n*r1,同理求出另外两个,加和得到sum,用abs(sum)%lcm就得到所求的数

题解状态:

164K,32MS,C++,991B

#include<cmath>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<numeric>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<list>
#include<stack>
using namespace std;
#define lim 3//从中国剩余定理模版改过来的,这里就沿用了一下
int rm[3];//remainder,记录三个余数
const int dv[3] = { 23,28,33 };//divisor,作为除数的三个数
const int lcm = 23 * 28 * 33;//least common multiple,拼写没错吧?这是三个数的最小公倍数,因为三个数互质,因此直接乘即可

int exgcd(int a, int b, int &x, int &y)//近期用烂了的扩展欧几里德,之前因为背错int ans那行折腾了一阵子,这里只能算算法的一部分
{
	if (!b) {
		x = 1, y = 0;
		return a;
	}
	int ans = exgcd(b, a%b, x, y);
	int t = x;
	x = y;
	y = t - a / b*y;
	return ans;
}
int fun(int a, int b, int c, int d)
{
	rm[0] = a, rm[1] = b, rm[2] = c;//只有三个数,就没用for
	int sum = 0;
	for (int p = 0; p < lim; p++) {
		int x, y;
		exgcd(lcm / dv[p], dv[p], x, y);//这里仅需要x,并且用lcm/dv[p]替代其余两个数的积,另外虽然公式里第二个参数应为-dv[p],但由于扩展欧几里德是在正数范围内解决问题,因此用正数即可
		sum += lcm / dv[p] * x*rm[p];
	}
	sum = sum%lcm - d;
	while (sum <= 0)
		sum += lcm;//参考讨论版的数据,找不到更好的方法控制范围,就用这个传统的办法了
	return sum;
}
int main(void)
{
	//freopen("vs_cin.txt", "r", stdin);

	int a, b, c, d,times=0;
	while (~scanf("%d%d%d%d", &a, &b, &c, &d) && a != -1 && b != -1 && c != -1 && d != -1) {//input
		printf("Case %d: the next triple peak occurs in %d days.\n", ++times,fun(a, b, c, d));//output//因为忘记输出有格式要求而wa了一次
	}
}

EOF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值