[JZOJ6065]【NOI2019模拟2019.3.18】One?One!【数学】【多项式】

博客围绕一个数学问题展开,要求计算特定多项式的值。通过上下同乘9进行转化,将问题写成卷积形式,可用FFT或NTT解决。对于剩余余数,因除出答案不大且数据随机,可暴力取每段前15位用10^15 - 1去除,在llogl时间复杂度完成计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

在这里插入图片描述
在这里插入图片描述

Solution

这道题虽然相对套路,但还是蛮考人的

我们要求的实际上就是∑d⌊n111...1⌋(d个1)\sum\limits_{d} {\lfloor{n\over 111...1}\rfloor}(d个1)d111...1nd1
有一个套路就是上下同时乘9
N=9nN=9nN=9n
那就变成了∑d≥2⌊N10d−1⌋\sum\limits_{d\geq 2}{{\lfloor{N\over 10^d-1}\rfloor}}d210d1N

考虑将NNN写成10dx+y10^dx+y10dx+y的形式,那么⌊N10d−1⌋=x+⌊x+y10d−1⌋{\lfloor{N\over 10^d-1}\rfloor}=x+{\lfloor{x+y\over 10^d-1}\rfloor}10d1N=x+10d1x+y

相当于对于我们将N分割成若干长度为d的段,每一段会给后面的所有段都贡献

先不考虑后面的余数
对于第i个位置,它会对i-d,i-2d,i-3d,i-4d都贡献
我们如果枚举d暴力算的话复杂度显然不能接受

仔细观察,实际上它可以写成卷积的形式,即-kd与i的卷积,于是可以用FFT或NTT解决(结果不是很大)
那么前面的部分就算完了,考虑最后剩下来的余数
我们枚举d,暴力求解的话我们需要对于每一段都加在一起再高精度除法,这显然也不能接受

然而我们发现除出来答案不会很大,并且由于数据随机,有很大概率只有最高那几位是有用的。

那我们可以暴力取每一段的前15位,然后直接用10^15-1去除即可。
注意10^15并没有超过long long,而一个高精度数除以long long是容易做的。

这样我们就在llog⁡ll\log lllogl的时间复杂度做完了。

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 500205
#define M 524288
#define L 19
#define LL long long
#define LT unsigned long long
#define mo1 4294967296
#define mo 998244353
using namespace std;
int n,a[N],bit[M+1];
LT sv;
LL u1[M+1],u2[M+1],wi[M+1],ny,c[101];
LL ksm(LL k,LL n)
{
	LL s=1;
	for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
	return s;
}
void prp()
{
	wi[0]=1,wi[1]=ksm(3,(mo-1)/M);
	fo(i,1,M) 
	{
		bit[i]=(bit[i>>1]>>1)|((i&1)<<(L-1));
		wi[i]=wi[i-1]*wi[1]%mo;
	}
	ny=ksm(M,mo-2);
}
void NTT(LL *a,bool pd)
{
	fo(i,0,M-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
	LL v;
	for(int h=1,m=2,lim=M>>1;m<=M;lim>>=1,h=m,m<<=1)
	{
		for(int j=0;j<M;j+=m)
		{
			fo(i,0,h-1)
			{
				v=((!pd)?wi[i*lim]:wi[M-i*lim])*a[i+j+h]%mo;
				a[i+j+h]=(a[i+j]-v+mo)%mo;
				a[i+j]=(a[i+j]+v)%mo;
			}
		}
	}
	if(pd) fo(i,0,M-1) a[i]=a[i]*ny%mo;
}
int main()
{
	cin>>n>>sv;
	sv%=mo1;
	LT ua=747796405,ub=1403630843;
	fo(i,1,n)
	{
		a[n-i]=(sv/1024)%10;
		sv=(sv*ua%mo1-ub+mo1)%mo1;
	}
	fod(j,n-1,0)
	{
		a[j]*=9;
		a[j+1]+=a[j]/10;
		a[j]%=10;	
	}
	fo(j,0,n-1)	a[j+1]+=a[j]/10,a[j]%=10;	
	while(a[n]>0) n++;
	while(n>1&&a[n-1]==0) n--;
	prp();
	fo(i,2,n) for(int j=1;i*j<=n;j++) u2[n-i*j]++;
	fo(i,0,n-1) u1[i]=a[i];
	NTT(u1,0),NTT(u2,0);
	fo(i,0,M-1) u1[i]=u1[i]*u2[i]%mo;
	NTT(u1,1);
	fo(i,0,n-1) u1[i]=u1[i+n];
	fo(i,n,M-1) u1[i]=0;
	fo(j,0,n-1) u1[j+1]+=u1[j]/10,u1[j]%=10;
	while(u1[n]>0)
	{
		while(u1[n]) n++;
		fo(j,0,n-1) u1[j+1]+=u1[j]/10,u1[j]%=10;
	}
	while(n&&!u1[n-1]) n--;
	fo(i,2,n)
	{
		int w=min(i,15),e=w;
		memset(c,0,sizeof(c));
		for(int j=0;j*i<=n+1;j++)
		{
			int l=(j+1)*i-w;
			fo(p,0,w-1) c[p]+=a[l+p];		
		}
		fo(j,0,e-1) c[j+1]+=c[j]/10,c[j]%=10;
		while(c[e]>0)
		{
			while(c[e]>0) e++;
			fo(j,0,e-1) c[j+1]+=c[j]/10,c[j]%=10;
		}
		
		LL vl=9,sm=0,al=0;
		fo(j,1,w-1) vl=vl*(LL)10+9;
		fod(j,e-1,0) 
		al=(al*(LL)10+c[j]),sm=(sm*(LL)10+al/vl),al%=vl;
		for(int c1=0;sm;c1++,sm/=10) u1[c1]+=sm%10;
		
	}
	fo(j,0,n-1) u1[j+1]+=u1[j]/10,u1[j]%=10;
	while(u1[n]) n++;
	if(n==0) printf("0\n");
	else
	{
		fod(i,n-1,0) printf("%d",u1[i]);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值