bzoj1213: [HNOI2004]高精度开根

纪念一下这道让我交了无数发的恶心高精度题
这是一中模拟赛中的一道题

题解:

我这道题刚开始打的是普通高精乘,然后进行了如下几步改动:
1.用FFT,但是T了
2.压位,过了一中数据,但在bzoj上WA了
3.考虑到精度误差,把FFT换成NTT,结果还是WA
4.问老师要来了数据,发现有一个点n是0,要特判,然后洛谷A了,bzojT了
5.发现压位最多只能压3位,3位以上就会WA,就请教bzoj群上大佬,说要把模数换得大一点。我用素数判断的程序找出了一些比998244353大的,并且符合减1后能被2^k整除的模数,结果有的逆元很难找出,有的不够大,于是,找不出更好的模数,只能放弃从压位这里优化
6.突然想到可以用内联函数,但并没有什么用处,还是T掉
7.我是从高位到低位枚举要填的数的,容易发现,在填高位的时候,低位都是0,这里可以优化,然后终于过了

标程:

#include<bits/stdc++.h>
typedef long long ll;
const int K=3,N=40003/K,M=pow(10,K),p=998244353,g=3,gi=332748118;
int m,i,l,r,L,lim,re[N],f[11],a[N],b[N],inv;
char s[10003];
struct NUM{
    int t,a[N];
}x,A;
inline int pls(int x,int y){x+=y;return x>=p?x-p:x;}
inline int dec(int x,int y){x-=y;return x<0?x+p:x;}
inline int mul(int x,int y){return (ll)x*y%p;}
inline int pow(int x,int y){
    int ans=1;
    for (;y;y>>=1,x=mul(x,x))
        if (y&1) ans=mul(ans,x);
    return ans;
}
inline void ntt(int *A,int opt){
    for (int i=0;i<lim;i++)
        if (i<re[i]) A[i]^=A[re[i]],A[re[i]]^=A[i],A[i]^=A[re[i]];
    int pp=p-1>>1;
    for (int mid=1;mid<lim;mid<<=1,pp>>=1){
        int wn=pow((opt==1)?g:gi,pp);
        for (int R=mid<<1,j=0;j<lim;j+=R){
            int w=1;
            for (int k=0;k<mid;k++,w=mul(w,wn)){
                int x=A[j+k],y=mul(w,A[j+(k|mid)]);
                A[j+k]=pls(x,y);
                A[j+(k|mid)]=dec(x,y);
            }
        }
    }
    if (opt==-1)
        for (int i=0;i<lim;i++) a[i]=mul(a[i],inv);
}
inline NUM mul(NUM x,NUM y,int t1,int t2){
    for (int i=0;i<x.t-t1;i++) a[i]=x.a[i+t1];
    for (int i=0;i<y.t-t2;i++) b[i]=y.a[i+t2];
    lim=1;L=0;
    for (;lim<x.t+y.t-t1-t2;lim<<=1,L++);
    for (int i=0;i<lim;i++) re[i]=(re[i>>1]>>1)|((i&1)<<(L-1));
    for (int i=x.t-t1;i<lim;i++) a[i]=0;
    for (int i=y.t-t2;i<lim;i++) b[i]=0;
    inv=pow(lim,p-2);
    ntt(a,1);ntt(b,1);
    for (int i=0;i<lim;i++) a[i]=mul(a[i],b[i]);
    ntt(a,-1);x.t+=y.t;
    for (int i=0;i<=t1+t2;i++) x.a[i]=0;
    for (int i=t1+t2;i<x.t;i++) x.a[i]+=a[i-t1-t2],x.a[i+1]=x.a[i]/M,x.a[i]%=M;
    while (x.t && !x.a[x.t-1]) x.t--;
    return x;
}
inline NUM pow(NUM x,int y,int t1){
    NUM z;
    int t2=0;
    if (t1<0) t1=0;
    z.t=z.a[0]=1;
    for (;y;y>>=1,x=mul(x,x,t1,t1),t1<<=1)
        if (y&1) z=mul(z,x,t2,t1),t2+=t1;
    return z;
}
inline bool operator >=(NUM x,NUM y){
    if (x.t!=y.t) return x.t>y.t;
    for (int i=x.t-1;i>=0;i--)
        if (x.a[i]!=y.a[i]) return x.a[i]>y.a[i];
    return 1;
}
inline void print(NUM x){
    for (int i=x.t-1;i>=0;i--){
        if (i<x.t-1){
            int k=(int)log10(x.a[i])+1;
            if (!x.a[i]) k=0;
            for (int j=0;j<K-k;j++) putchar('0');
        }
        if (x.a[i]) printf("%d",x.a[i]);
    }
}
int main(){
    scanf("%d\n",&m);
    gets(s);//在洛谷里一定要scanf
    f[0]=1;
    for (i=1;i<9;i++) f[i]=f[i-1]*10;
    A.t=strlen(s);
    if (A.t==1 && s[0]=='0'){
        putchar('0');
        return 0;
    }
    for (i=A.t-1;i>=0;i--) A.a[(A.t-1-i)/K]+=(s[i]^48)*f[(A.t-1-i)%K];
    A.t=((A.t-1)/K)+1;
    x.t=(A.t-1)/m+1;
    for (i=x.t-1;i>=0;i--){
        l=0;r=M-1;
        while (l<r){
            x.a[i]=l+r+1>>1;
            if (A>=pow(x,m,i)) l=x.a[i];
            else r=x.a[i]-1;
        }
        x.a[i]=l;
    }
    print(x);
}

这个程序竟然还没普通的高精乘快,无语。。。可能是因为他们都压8位吧,而我只能压3位
这题虽然花了我不少时间,但还是有很多收获的,是道不错的题目

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值