2022/6/27随笔
//P1249 最大乘积
//数论+贪心+高精度
P1249 最大乘积 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
数论蒟蒻,无从下手
洛谷上大佬的题解:
用贪心和数论来找规律,最后使用高精度乘法
我们尽可能的把n分成更多份(都大于1)那样乘积最大
对于余数:从大数开始向前,依次分配1
例如15:s=2+3+4+5+6刚好大于15,s-15=5,所以把5去掉。
又例如13:s=2+3+4+5刚好大于13,s-13=1,所以去掉2,并把5加1,即3 4 6。
我们分出来了2,3,4......n这n-1个数然后余数是K(1<=K<=n)。
1、如果K==n,我们需要进行两轮分配,意思是从n分配到2还剩下1(我们有n-1个数,),需要再回去把1分配给最大的数(也就是n+1)最终得到3,4,5......n+2这也就应对了上文题解中的情况2;
(即:全部加上一轮后,还余下1,加给最大项即可)
以例5:n-1项加一轮后:3,4,5,此时还余1,给最大项,即:3,4,6(同上述面方法的结果)
2、如果K<n,我们进行一轮分配就好因为我们的余数是K,那么分配到n+1-K就停止了,
拿15举例,先分配出了2,3,4,5余1,我们分配到5就停止了,(n+1-K=5)。
对于上篇题解他先分配出2,3,4......n,n+1。因为我们分出来n-1个数余数是K,而分配出n+1后会造成数不够,还差n+1-K,那么这也就对应前两行我们推导出的结果,去掉n+1-K,就相当于分配到了n+1-K,应为n+1-K经过分配后变成了,n+2-K,这不就相当于去掉了吗。所以这相当于找规律了。
若:2,3,4……n-1(4)余K(6),先分配到n+1:2,3,4……n+1(6)多n+1-K(5)
去掉n+1-K(5)的对应项
最后总结:
把数字(num)拆分成s=2……n-1,从第n项开始,num-s>0,记录n-1项的余数为K
先用余数进行第一轮加值(从大向小加),若第一轮后仍然有余数,则全部加给最后一项
得到字符串后,进行高精度乘法
注意:当n小于5时,本身就是最优解
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int L=500; 5 //高精度乘法 6 string mul(string a,string b) 7 { 8 string s; 9 int na[L],nb[L],nc[L],La=a.size(),Lb=b.size(); 10 fill(na,na+L,0); 11 fill(nb,nb+L,0); 12 fill(nc,nc+L,0); 13 for(int i=La-1;i>=0;i--) na[La-i]=a[i]-'0'; 14 for(int i=Lb-1;i>=0;i--) nb[Lb-i]=b[i]-'0'; 15 for(int i=1;i<=La;i++) 16 { 17 for(int j=1;j<=Lb;j++) 18 { 19 nc[i+j-1]+=na[i]*nb[j]; 20 } 21 } 22 for(int i=1;i<=La+Lb;i++) 23 { 24 nc[i+1]+=nc[i]/10; 25 nc[i]%=10; 26 } 27 if(nc[La+Lb]) s+=nc[La+Lb]+'0'; 28 for(int i=La+Lb-1;i>=1;i--) 29 { 30 s+=nc[i]+'0'; 31 } 32 return s; 33 } 34 //把整形转换为字符串 35 string f(int a) 36 { 37 char ch[10],t; 38 int i=0; 39 int j; 40 while(a) 41 { 42 ch[i]=a%10+'0'; 43 a/=10; 44 i++; 45 } 46 ch[i]='\0'; 47 for(i=i-1,j=0;j<=i/2;j++,i--) 48 { 49 t=ch[i]; 50 ch[i]=ch[j]; 51 ch[j]=t; 52 } 53 return ch; 54 } 55 56 int n,a[1005]; 57 string s[1005]; 58 int c=1; 59 int main() 60 { 61 string m="1"; 62 cin>>n; 63 //特判,如果n小于5,自己本身就是最优解 64 if(n<=4) 65 { 66 cout<<n<<endl<<n; 67 return 0; 68 } 69 for(int i=2;i<=n;i++) 70 { 71 if(n>=i) 72 { 73 n-=i; 74 a[c++]=i; 75 s[c-1]=f(i); 76 } 77 else 78 break; 79 } 80 //如果有余数,从最高位开始加一 81 for(int i=c-1;i>=1;i--) 82 { 83 if(n>0) 84 { 85 a[i]++; 86 s[i]=f(a[i]); 87 n--; 88 } 89 } 90 //分过一轮后,如果还多,就让最后一个数加一,余数<=n 91 if(n>0) 92 { 93 a[c-1]++; 94 s[c-1]=f(a[c-1]); 95 } 96 //高精度加 97 for(int i=1;i<c;i++) 98 { 99 //输出分出的整数 100 cout<<a[i]<<" "; 101 m=mul(s[i],m); 102 } 103 cout<<endl<<m; 104 return 0; 105 }

//P1226 【模板】快速幂||取余运算
复习一下模板
P1226 【模板】快速幂||取余运算 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
若幂指数不为0,幂指数变为原来的一半,基数等于基数的平方
计算所有幂指数为奇数项的乘积。
使用位运算提速
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 #define ll long long 5 ll quick(ll base,ll power,ll c) 6 { 7 ll r=1; 8 while(power>0) 9 { 10 if(power&1) 11 { 12 r=r*base%c; 13 } 14 power>>=1; 15 base=base*base%c; 16 } 17 18 return r; 19 } 20 int main() 21 { 22 ll a,b,c; 23 cin>>a>>b>>c; 24 cout<<a<<"^"<<b<<" "<<"mod"<<" "<<c<<"="<<quick(a,b,c)<<endl; 25 return 0; 26 }

//P1045 [NOIP2003 普及组] 麦森数
//数论+快速幂+高精度
P1045 [NOIP2003 普及组] 麦森数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

1、输入
2、计算长度,使用数学函数log10()
3、借助高精度乘法(整形)与快速幂结合,求值(只计算后五百位)
4、按题意减一
5、输出500位,50个一行
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstring> 5 using namespace std; 6 int r[1005],base[1005],t[1005]; 7 int n; 8 void result_1() 9 { 10 memset(t,0,sizeof(t)); 11 for(int i=1;i<=500;i++) 12 { 13 for(int j=1;j<=500;j++) 14 { 15 t[i+j-1]+=base[i]*r[j]; 16 } 17 } 18 for(int i=1;i<=500;i++) 19 { 20 t[i+1]+=t[i]/10; 21 t[i]%=10; 22 } 23 //把t的值赋给r,cstring库里的函数 24 memcpy(r,t,sizeof(r)); 25 } 26 void result_2() 27 { 28 memset(t,0,sizeof(t)); 29 for(int i=1;i<=500;i++) 30 { 31 for(int j=1;j<=500;j++) 32 { 33 t[i+j-1]+=base[i]*base[j]; 34 } 35 } 36 for(int i=1;i<=500;i++) 37 { 38 t[i+1]+=t[i]/10; 39 t[i]%=10; 40 } 41 memcpy(base,t,sizeof(base)); 42 } 43 int main() 44 { 45 cin>>n; 46 //log10的返回值位double 47 cout<<(int)(log10(2)*n+1)<<endl; 48 base[1]=2; 49 r[1]=1; 50 //只计算500位 51 while(n) 52 { 53 if(n&1) 54 result_1();//奇数项相乘 55 n>>=1; 56 result_2();//底数等于底数的平方 57 } 58 r[1]--;//题目要求 59 for(int i=500;i>=1;i--) 60 { 61 if(i%50==0&&i!=500) 62 //先换行再输出,初始值为500,500-551是50个数了 63 cout<<endl<<r[i]; 64 else 65 cout<<r[i]; 66 } 67 return 0; 68 }


浙公网安备 33010602011771号