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 }

 

posted @ 2022-06-27 19:59  格蕾  阅读(45)  评论(0)    收藏  举报