数论(4) | 整数与除数刷题日记
【洛谷题单】整数与除数
P1516 青蛙的约会
P1082 [NOIP2012 提高组] 同余方程
P3951 [NOIP2017 提高组] 小凯的疑惑
P3811 乘法逆元
P4942 小凯的数字
P3868 [TJOI2009]猜数字
P4777 扩展中国剩余定理(EXCRT)
P2158 [SDOI2008]仪仗队
P5560 [Celeste-B]Golden Feather
其中P1516,P1082,P3811,P3868,P4777为模板题,本篇简单介绍剩下4篇解题思路。
P3951 [NOIP2017 提高组] 小凯的疑惑
【题目描述】
小凯手中有两种面值的金币,两种面值均为正整数且彼此互素。每种金币小凯都有无数个。在不找零的情况下,仅凭这两种金币,有些物品他是无法准确支付的。现在小 凯想知道在无法准确支付的物品中,最贵的价值是多少金币?
【思路】
用拓展欧几里得码了半天后来发现是道找规律直接哭了QAQ
两个数字互素是解题关键
打开excel找规律,我们把所有3k染红,3k+7染黄,3k+14染绿,3k+21染蓝。我们发现每两个颜色之间隔2个位置(废话)不同颜色会阶梯状的递下去。要使2个空缺位填满,只要保证数字在2*7以后即可。而第一个染不上颜色的空格就是2*7-3=11,即(a-1)*b-a,也可以写成(a-1)*(b-1)-1.
等等,如果数字是5和7,那不同颜色就不是阶梯状下去了吧?这时候就要用到互质这个条件了。因为gcd(5,7)=1,可以想象,两个5中间的四个空格,总是可以用四个7来“填满”,最终答案仍然是(a-1)*(b-1)-1.
代码就没什么好贴的了......
P4942 小凯的数字
【题目描述】
小凯有一天突发奇想,写下了一串数字:l(l+1)(l+2)...(r-1)r
例如:l=2,r=55时,数字为:2345
l=8,r=12时数字为:89101112
小凯很喜欢数字9,所以他想问你他写下的数字除以9的余数是多少?
数据范围:l,r<10^12
【思路】
这道题要用到一个非常巧妙的规律,当i>0时,10^i模9的值恒为1(说人话就是,一百一千一万一千万这种数,模9之后答案肯定是1)。进而可以推出,k*10^i模9的值恒为k。
比如:l=8,r=12,s=89101112
把s拆成s=8*10000000+9*1000000+10*10000+11*100+12
s%9=(8%9)+(9%9)+(10%9)+(11%9)+(12%9)=(8+9+10+11+12)%9
结论:对[l,r]高斯求和再模9即可
P2158 [SDOI2008]仪仗队
【题目描述】
作为体育委员,C君负责这次运动会仪仗队的训练。仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。现在,C君希望你告诉他队伍整齐时能看到的学生人数。
【思路】
把原点视为(0,0),第0行和第0列只能看到(0,1)和(1,0).
当a>0&&b>0时,如果(a,b)可以看到,则(2a,2b)(3a,3b)肯定看不到
对于一个点(a,b)如果gcd(a,b)>1,则(a,b)一定会被(a/gcd(a,b),b/gcd(a,b))挡住
因此要判断(a,b)是否能看到,只需要判断gcd(a,b)是否等于1
我们现在要做的就是在(1,1)到(n-1,n-1)的方阵中,寻找有几个gcd(a,b)=1的点。
开一个数组f[i],表示在(1,1)到(n-1,n-1)的方阵中,(a,b)的最大公约数为i的位置个数
直接求f[i]比较难,不妨再开一个数组g[i],表示在(1,1)到(n-1,n-1)的方阵中,(a,b)的公约数包含i的位置个数(注意只要公约数包含i就可以,不需要是最大公约数)
g[i]=((n-1)/i)*((n-1)/i)
然后可以通过g[i]去推f[i],例如,f[2]表示以2为最大公约数的位置数量,g[2]表示以2位公约数的数量。因此g[2]=f[2]+f[4]+f[6]+...。同理,g[3]=f[3]+f[6]+f[9]+...;g[4]=f[4]+f[8]+f[12]+...倒过来递推就可以了
【代码】
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 int main(){ 5 LL n,ans,g[40007]={0}; 6 cin>>n; 7 ans=(n-1)*(n-1); 8 for (int i=n-1;i>=2;i--){ 9 g[i]=((n-1)/i)*((n-1)/i);//f[i] 10 for (int j=2*i;j<=n;j+=i) g[i]=g[i]-g[j]; 11 ans-=g[i]; 12 } 13 if (n==1) cout<<0; 14 else cout<<ans+2; 15 return 0; 16 }
P5560 [Celeste-B]Golden Feather
【题目描述】
(题目真是写的乱七八糟,读了N编才读懂)
有n个点,其中第i个点的魔力为f(i),f(i)=(i+1)^2-1,沟通两个点所需要的能量为这两个点的魔力的gcd。求将n个点连起来最小代价。
题目有T组数据,每组数据给个n,需要我们算n个点的代价。
【思路】
不会真的有人准备写最小生成树吧(n<10^18)
首先把(i+1)^2-1变化成i*(i+2),好看点hh
进入正题,这道题用的还是递推的思想。比如我们现在已经知道了k-1个结点连起来的最小代价,现在要把第k个节点连进去(k≥2)。最好的情况一定是在前k-1个点里,有一个点和k节点相连gcd=1的。
也就是说我们尽可能要在1...k-1个点中找到一个点j,使得gcd(f(j),f(k))=1。
为了方便理解,先把结论告诉大家:只有当k=4和k=10的时候,我们找不到点j,其他时候我们总能找到点j。也就是说,只有n=4或者n=10需要我们单独算,其他情况下直接输出n-1就可以了(连接每个点的代价都是1)
那么怎么去找点j呢?
(1)当j=k-1时,f(j)=(k-1)*(k+1),f(k)=k*(k+2)。我们知道除了2和3,剩下的素数都是不相邻的。因此,当k>3时,k和k-1互质,k和k+1互质,k+2和k+1互质。要让gcd(f(j),f(k))=1不成立,只有两种情况,一是k≤3,二是k+2和k-1互质
首先排除第一种情况,f(1)=3,f(2)=8,f(3)=15,都是互质的
k+2和k-1就不能保证互质了,当(k+2)是3的倍数时,gcd(k+2,k-1)显然大于等于3。那么这个时候,只能去换一个j值。
(2)当j=k-2时,f(k-2)=(k-2)*k,与f(n)必定不互质。
(3)f(k-3)=(k-3)*(k-1),k-3与k一定互质(因为剩下来的都是是除以3余1的),k-1与k+2不可能互质。
(4)f(k-4)=(k-4)*(k-2),因为k>4,又可得k为奇数时,k-4,k-2,k,k+2互质。所以范围又缩小到4,10,16,22……
经过这些论证,我们已经可以发现绝大多数k都是能找到互质的点j的了,但论证到此还不充分,一步一步推下去也太累了,因此我们尝试下用程序来推。(因为大多数k只需要两三下就可以找到j,所以时间复杂度约等于O(n))
1 #include <bits/stdc++.h>//试验至1e8,再大O(n)吃不住了 2 using namespace std; 3 typedef long long LL; 4 LL gcd(LL a,LL b){ 5 LL t=a%b; 6 while (t){ 7 a=b;b=t; 8 t=a%b; 9 } 10 return b; 11 } 12 LL a[100000001]; 13 int main(){ 14 a[1]=3; 15 for (int i=2;i<=100000000;i++){ 16 a[i]=(long long)(i+2)*i; 17 bool h=true; 18 for (int j=1;j<i;j++) 19 if (gcd(a[i],a[j])==1){ 20 h=false; 21 break; 22 } 23 if (h) printf("%lld\n",i); 24 } 25 return 0; 26 }
运行结果是只输出了4和10。到此为止我们可以得出10^8次以内除了4和10其他k都能找到j了。虽然题目的范围是10^18次,但是我们可以大胆猜测,10^18以内其他数字都能找到j了。
试试嘛,大不了罚时hh
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 int main(){ 5 int T; 6 cin>>T; 7 while (T--){ 8 LL n; 9 cin>>n; 10 if (n==4||n==10) printf("%lld\n",n+1); 11 else printf("%lld\n",n-1); 12 } 13 return 0; 14 }
最终还是过了的:)