HDU 6137 Engineering of the Clones(快速幂+NTT)

52 篇文章 0 订阅
9 篇文章 0 订阅

Description

给出一正整数 A 的质因子分解形式A=pr11pr22...prnn,其中 |rirj|1 ,问 1A1 i=1npi 进制下小数点后第 k 位的值

Input

首先输入一整数T表示用例组数,每组用例首先输入两整数 n k,然后输入 n 个互不相同的素数pi,之后输入 n 个整数ri

(1n106,1k<max{r21,r22,...,r2n},1i=1npi106,1ri5104)

Output

输出 1A1 i=1npi 进制下小数点后第 k 位的值

Sample Input

1

2 1

2 5

2 2

Sample Output

0

Solution

1A1=1+111A=1+i=0(1A)i=i=1(1A)i

L=max{r1,...,rn} ,则 1A base=i=1npi 进制下可以表示为一个 L 位小数,第L位为 x=pLr11...pLrnn

由于 1k<L2 (1A)L+1=xL+1baseL2L<baseL2+1basek ,故在算第 k 位数时只需要算xm即可,其中 m=kL+1L ,而不用担心 i=L+1(1A)i 产生的进位影响,计算 xm 用快速幂加 NTT 即可

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef long double ld;
const int maxbit=17,maxlen=1<<maxbit,maxn=100005;
const ll mod=4179340454199820289,g=3;
ll wn[maxlen],inv2[maxbit+1];
ll mul(ll a,ll b)
{
    return (a*b-(ll)(a/(ld)mod*b+1e-3)*mod+mod)%mod;
}
ll mod_pow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}
void init()
{
    wn[0]=1,wn[1]=mod_pow(g,(mod-1)>>maxbit);
    for(int i=2;i<maxlen;i++)wn[i]=mul(wn[i-1],wn[1]);
    inv2[0]=1,inv2[1]=(mod+1)/2;
    for(int i=2;i<=maxbit;i++)inv2[i]=mul(inv2[i-1],inv2[1]);//预处理2^i的逆元 
}
void ntt(ll *x,int len,int sta) 
{
    for(int i=0,j=0;i<len;i++)
    {
        if(i>j)swap(x[i],x[j]);
        for(int l=len>>1;(j^=l)<l;l>>=1);
    }
    for(int i=1,d=1;d<len;i++,d<<=1)
        for(int j=0;j<len;j+=d<<1)
            for(int k=0;k<d;k++)
            {
                ll t=mul(wn[(maxlen>>i)*k],x[j+k+d]);
                x[j+d+k]=x[j+k]-t<0?x[j+k]-t+mod:x[j+k]-t;
                x[j+k]=x[j+k]+t>=mod?x[j+k]+t-mod:x[j+k]+t;
            }
    if(sta==-1)
    {
        reverse(x+1,x+len);
        int bitlen=0;
        while((1<<bitlen)<len)bitlen++;
        ll val=inv2[bitlen];
        for(int i=0;i<len;i++)x[i]=mul(x[i],val);
    }
}
void Deal(ll *x,int len,int base)
{
    for(int i=0;i<len;i++)
        if(x[i]>=base)
            x[i+1]+=x[i]/base,x[i]%=base;
}
int T,n,p[maxn],r[maxn];
ll k,A[maxlen],B[maxlen],Ans[maxlen];
ll Solve(int L,int m,int pos,int x,int base)
{
    init();
    memset(A,0,sizeof(A));
    memset(Ans,0,sizeof(Ans));
    A[0]=x,Ans[0]=1;
    int len=2;
    while(m)
    {
        ntt(A,len,1);
        if(m&1)
        {
            ntt(Ans,len,1);
            for(int i=0;i<len;i++)Ans[i]=mul(Ans[i],A[i]);
            ntt(Ans,len,-1);
            Deal(Ans,len,base);
        }
        for(int i=0;i<len;i++)A[i]=mul(A[i],A[i]);
        ntt(A,len,-1);
        Deal(A,len,base);
        len<<=1;
        m>>=1;
    }
    return Ans[pos];
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%I64d",&n,&k);
        int base=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&p[i]);
            base*=p[i];
        }
        for(int i=1;i<=n;i++)scanf("%d",&r[i]);
        int L=r[1];
        for(int i=1;i<=n;i++)L=max(L,r[i]);
        int x=1;
        for(int i=1;i<=n;i++)
            if(r[i]<L)x*=p[i];
        k--;
        printf("%I64d\n",Solve(L,k/L+1,L-1-k%L,x,base));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值