P4157 [SCOI2006]整数划分(打表,FFT高精乘,debug)

3 篇文章 0 订阅
2 篇文章 0 订阅

P4157 [SCOI2006]整数划分

分析:

​ 要使乘积最大,考虑到要使指数大一些, 4 = 2 2 4=2^2 4=22 ,因此两个 2 2 2 可由一个 4 4 4 替换, 3 2 > 2 3 3^2 > 2^3 32>23 ,所以要使 3 3 3 尽量多。

对于最后一个数取什么要做分类讨论:

  • n % 3 = 0 n\% 3=0 n%3=0​ ,取 3 3 3
  • n % 3 = = 1 n\%3==1 n%3==1 ,去掉一个 3 3 3换成 4 4 4
  • n % 3 = = 2 n \%3==2 n%3==2 , 取 2 2 2

然后就是处理高精度乘法了,这里用的是 F F T FFT FFT​​ , n < = 31000 n<=31000 n<=31000​​ , n / 3 < 10400 n/3<10400 n/3<10400​​ ,因为并不知道是哪两个确切的数相乘,这里采取的方法是将 3 32 ( 2 5 ) 3^{32(2^5)} 332(25)​​ , 3 64 ( 2 6 ) 3^{64(2^6)} 364(26)​​ , 3 128 ( 2 7 ) 3^{128(2^7)} 3128(27)​ … 3 8192 ( 2 12 ) 3^{8192(2^{12})} 38192(212)​ 通过打表的方式得出来,然后再依次比较 n / 3 n/3 n/3​ 与 8192 8192 8192​ 到 32 32 32​ 的大小,若大于等于,则结果乘上 3 b a s e 3^{base} 3base​​ ,最后还有多余,直接在结果里做乘法即可。

弱弱的吐槽一下: p r e ( ) pre() pre()​ ,没有对 A A A​ 和 B B B​ 清空,根本就没想到清空, d e b u g debug debug d e de de 了 一个多小时,不讲恶徳~~~

#include<bits/stdc++.h>
#define int long long
using namespace std;

typedef long long ll;
const int N = (1<<21)+5;
const double PI=acos(-1);
struct Complex
{
    double x, y;
    Complex operator+(const Complex &o) const{return{x+o.x,y+o.y};}  
    Complex operator-(const Complex &o) const{return{x-o.x,y-o.y};}  
    Complex operator*(const Complex &o) const{return{x*o.x-y*o.y,x*o.y+y*o.x};} 
}A[N],B[N],C[12][N],t[N];
int rev[N];
void init(int k)
{
    int s=1<<k;
    for(int i=1;i<s;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
}
void fft(Complex *a,int n,int inv)
{
    for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int len=1;len<n;len<<=1)
    {
        Complex Wn=Complex({cos(PI/len),inv*sin(PI/len)});
        for(int i=0;i<n;i+=len*2)
        {
            Complex w=Complex({1,0});
            for(int j=0;j<len;j++,w=w*Wn)
            {
                Complex x=a[i+j],y=w*a[i+j+len];
                a[i+j]=x+y,a[i+j+len]=x-y;
            }
        }
    }
    if(inv==-1) for(int i=0;i<n;i++) A[i].x = A[i].x/n+0.5; // 精度
}
// --------FFT
int ans[N], deep[15]; // deep 用来存位数
void pre(Complex *a, Complex *b,Complex *c, int &n, int m)
{
    // A, B 一定要清空,因为用到的不仅仅是 [0,n-1],而是 [0,s-1]
    memset(A,0,sizeof(A));
    memset(B,0,sizeof(B));
    for(int i=0;i<n;i++) A[i] = a[i];
    for(int i=0;i<m;i++) B[i] = b[i];
    int k=1, s=2;
    while(s < n+m-1) k++, s<<=1;
    init(k);
    fft(A,s,1); fft(B,s,1);
    for(int i=0;i<s;i++) A[i] = A[i]*B[i];
    fft(A,s,-1);
    memset(ans,0,sizeof(ans));
    for(int i=0;i<s;i++)
    {
	    ans[i] += (int)(A[i].x);
	    ans[i+1] += ans[i]/10;
	    ans[i] %= 10;
	}
	while(!ans[s] &&s >-1) s--;
    for(int i=0;i<=s;i++) c[i].x = (double)ans[i];
    n=s+1;
    /*cout<<n<<endl;
    for(int i=s;i>=0;i--) cout<<ans[i];
    cout<<endl;*/
}
void build() 
{   // 打表
    ll base = pow(3,32);
    int tot=0;
    while(base)
    {
        C[0][tot++].x = base%10;
        base /= 10;
    }
    //cout<<tot<<endl;
    deep[0] = tot;
    for(int i=1;i<=9;i++)
    {
        pre(C[i-1],C[i-1],C[i],tot,tot);
        deep[i] = tot;
        //cout<<tot<<endl;
    }
}
signed main()
{
    build();
    int n;
    cin>>n;
    int yu=1;
    if(n%3==1) yu=4, n-=4;
    if(n%3==2) yu=2, n-=2;
    int cnt=n/3;
    if(cnt<32)
    {
        int res = yu*pow(3,cnt);
        int tmp=res, len=0;
        while(tmp) len++, tmp /= 10;
        cout<<len<<endl<<res;
        return 0;
    }
    int p=8, base = (1<<13);
    // 找到第一个满足条件的
    while(cnt < base) base >>= 1, p--;
    cnt -= base;
    // Coplex t[] 用来暂存结果
    for(int i=0;i<deep[p];i++) t[i] = C[p][i];
    int cn=deep[p];
    while(p >= 0)
    {
        if(cnt >= base) 
        {
            pre(t, C[p], t, cn , deep[p]);
            cnt -= base;
        }
        base >>= 1;
        p--;
    }
    if(cnt) yu *= pow(3,cnt);
    memset(ans,0,sizeof(ans));
    for(int i=0;i<cn;i++)
    {
        ans[i] += (int)(t[i].x)*yu;
        ans[i+1] += ans[i]/10;
        ans[i] %= 10;
    }
    while(ans[cn]) 
    {
        ans[cn+1] += ans[cn]/10;
        ans[cn] %= 10;
        cn++;
    }
    while(!ans[cn] && cn > -1) cn--;
    cout<<cn+1<<endl;
    for(int i=cn;i>=max((ll)0,cn-99);i--) cout<<ans[i];

	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yezzz.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值