数论(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 }

最终还是过了的:)

posted @ 2021-07-04 14:14  奇思妙想张霁羊  阅读(140)  评论(0)    收藏  举报