寒假期间出的两个有意思的数论题

这个博客很久没更新了,我不希望它沦为僵尸博客。。所以今天更新一下,近期没有比较好的素材,于是就随便写了。还是关于OI题目的。也许以后会更cs相关的?

寒假期间给学弟出了一套数论题,一共四道,其中有两道是我觉得比较创新或者不错的,于是放在最前面,对于初学数论的同学来讲可能难度有点高。

 

 

第一题,小球碰撞;

寒假期间出的两个有意思的数论题_第1张图片

数据范围:0 < ??, ?? < 10^9, − 10^12 < ? < 10^12,保证答案的绝对值不超过10^18

题解:

寒假期间出的两个有意思的数论题_第2张图片

当然,也可以使用高中物理的二级结论,即两个小球黏在一起时动能最小,形式化地:

寒假期间出的两个有意思的数论题_第3张图片

标程如下

#include
#include
#include
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
#define LL long long
LL p, a, b;

LL exgcd(LL a, LL b, LL &x, LL &y)
{
	if(!b) { x = 1, y = 0; return a; }
	else { int r = exgcd(b, a%b, y, x); y -= x*(a/b); return r; }
}

int main()
{
	while (~scanf("%lld%lld%lld",&a,&b,&p))
	{
		LL va0, va1, vb0, vb1, u, v;
		LL d = exgcd(a, b, va0, vb0);
		if (p%d!=0) { puts("-1"); return 0; }
		va1 = va0*(p/d), vb1 = vb0*(p/d);
		u = b/d;
		v = -a/d;
		double tm = (1.0*vb1-va1)*d/(a+b);
		LL t = tm, tt;
		LL va, vb, res = -1;
		for(int i=-2;i<=2;++i)
		{
			tt = t+i;
			va = va1+u*tt;
			vb = vb1+v*tt;
			if (i==-2) res=a*va*va+b*vb*vb;
			else res = min(res, a*va*va+b*vb*vb);
		}
		printf("%lld", res>>1);
		if (res&1) printf(".5");
		puts("");
	}
	return 0;
}

常规的扩展欧几里得的题,例如青蛙的约会,对答案的要求都比较简单,例如x或y的最小正整数解。

这个题给出了不定方程求特解的通用的解法。

写出答案的函数表达式F(x,y),然后通解是x和y关于t的参数方程,即x=u(t), y=v(t),那么F(x,y)=F(u(t),v(t))=f(t),这样就将答案转化成了t的单变量函数,然后可以用线性规划、单变量函数求极值等方法找到t的理论最优值t0,对于一般的函数而言答案即在t0两边的整点处取得。

 

第二题,阶乘除法

寒假期间出的两个有意思的数论题_第4张图片

题解:

寒假期间出的两个有意思的数论题_第5张图片

寒假期间出的两个有意思的数论题_第6张图片

标程:

#include
#include
#include
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int MAXN = 100005;
int n, m, M;
int a[MAXN], b[MAXN], p[MAXN], pn;
long long w[MAXN];
int s[MAXN];
bool vis[MAXN];
void sieve()
{
	for (int i = 2; i*i>=1, a=1ll*a*a%c)
		if (b&1) res = 1ll*res*a%c;
	return res;
}

int main()
{
	scanf("%d%d%d",&n,&m,&M);
	sieve();
	int c, mc = 0;
	rep(i,1,n)
	{
		scanf("%d",&c);
		mc = max(mc, c);
		s[c] ++;
	}
	rep(i,1,mc) s[i] += s[i-1];
	solve(mc, 1);
	memset(s,0,sizeof s);
	mc = 0;
	rep(i,1,m)
	{
		scanf("%d",&c);
		mc = max(mc, c);
		s[c] ++;
	}
	rep(i,1,mc) s[i] += s[i-1];
	solve(mc, -1);
	int ans = 1;
	rep(i,1,pn) ans = 1ll*ans*ksm(p[i], w[i], M)%M;
	printf("%d\n", ans);
	return 0;
}

这个题属于和式化简的比较传统的题,只不过之前好像没有人出过一样的题目,所以我就挂上来了。

需要测试数据的私信我或者Email我即可。

 

 

 

你可能感兴趣的:(数论,题解)