【题解】P8350 [SDOI/SXOI2022] 进制转换
来描述一下 EI 做法。如有意会错误之处,望海涵。
不妨将题面中的 改为 。
为描述方便,提前声明最终时间复杂度为 。
并设二阈值 使 。
对于原题面中的 ,分别枚举其在两个进制下的低位、高位,将所求转写为如下和式:
其中 为所枚举低位。
而。
为使求和不超过题面所述上界 ,可考虑对 作限制 。此时 会恰取遍 中的每一个数。因此若在此条件下计算出上述和式,则我们只需枚举剩余不超过 项即可得到最终答案。
计算和式的关键在于将和式中的限制移项为:
我们可以考虑分别计算
而 。因此分别计算两数列下标在此区间内的值即可。
先考虑算 。先枚举 ,则由于 同阶,使 落在此区间内的 仅有 个,因此暴力枚举即可做到 的复杂度。
至于算 ,可以考虑逐位倍增计算:设我们当前计算出的 是在 的限制下,欲将之倍增至 。由于函数 具有良好的按位独立性质,可以得到如下递推式:
将 倍增至 方法类似。每次取 与 之较小者倍增,可保持 与 同阶,而使总复杂度做到 。
注意在计算 等函数时,不要由于快速幂或求各数位之和而使复杂度多带 ,具体实现可见代码。由于算法常数很小,目前是最优解。
#include<cstdio>
#include<numeric>
#include<cassert>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdlib>
using namespace std;
int read();
typedef long long ll;
#define fr(i,l,r) for(int i=(l);i<=(r);++i)
#define rf(i,l,r) for(int i=(l);i>=(r);--i)
#define fo(i,l,r) for(int i=(l);i<(r);++i)
#define foredge(i,u,v) for(int i=fir[u],v;v=to[i],i;i=nxt[i])
#define filein(File) freopen(File".in","r",stdin)
#define fileout(File) freopen(File".out","w",stdout)
const int N1=(1<<22)+5,N2=4782969+5,MOD=998244353;
ll qpow(ll a,int x) {
ll z=1;
for(;x;x>>=1,a=a*a%MOD)
if(x&1) z=z*a%MOD;
return z;
}
ll n,lim1,lim2,B1,B2;
int x,y,z,s1,s2,c1,c2;
ll f[N1+N2],g[N1+N2],tf[N1+N2];
void calcf() {
ll len1=1,len2=1;
f[N2]=1;
while(len1<B1||len2<B2) if(len1<len2) {
rf(i,len1-1,-len2+1) (f[i+len1+N2]+=f[i+N2]*y)%=MOD;
len1*=2;
} else {
ll val=qpow(x,len2)*z%MOD;
fr(i,-len2+1,len1-1) {
ll tmp=f[i+N2]*val%MOD;
(f[i-len2+N2]+=tmp)%=MOD;
(f[i-len2*2+N2]+=tmp*val)%=MOD;
}
len2*=3;
}
}
int d2[50],d3[30],cnt2,cnt3;
void init(ll x) {
cnt2=cnt3=0; ll i;
i=x; fo(j,0,44) cnt2+=d2[j]=i%2,i/=2;
i=x; fo(j,0,28) cnt3+=d3[j]=i%3,i/=3;
}
void inc() {
++d2[0]; ++d3[0]; ++cnt2; ++cnt3;
fo(j,0,44) if(d2[j]>=2) d2[j]=0,++d2[j+1],cnt2-=1;
else break;
fo(j,0,28) if(d3[j]>=3) d3[j]=0,++d3[j+1],cnt3-=2;
else break;
}
static ll pw2[50],pw3[60];
void calcg() {
init(0);
ll xB2=qpow(x,B2),xi=1;
for(ll i=0;i<lim2;++i) {
for(ll j=i*B2/B1;j<lim1&&i*B2-j*B1>-B2;++j)
(g[i*B2-j*B1+N2]+=xi*pw3[cnt3]%MOD*pw2[__builtin_popcountll(j)])%=MOD;
xi=xi*xB2%MOD;
inc();
}
}
int main() {
cin>>n>>x>>y>>z;
*pw2=1; fr(i,1,45) pw2[i]=pw2[i-1]*y%MOD;
*pw3=1; fr(i,1,56) pw3[i]=pw3[i-1]*z%MOD;
for(ll p1=1;p1<n;p1*=2) ++s1;
for(ll p2=1;p2<n;p2*=3) ++s2;
c1=s1+1>>1,c2=s2+1>>1;
B1=1; fr(i,1,c1) B1*=2;
B2=1; fr(i,1,c2) B2*=3;
lim1=n/B1; lim2=n/B2;
calcf(); calcg();
ll ans=0;
fr(i,-B2+1,B1-1) (ans+=f[i+N2]*g[i+N2])%=MOD;
ll lim=min(lim1*B1,lim2*B2);
init(lim);
ll pw=qpow(x,lim%(MOD-1));
for(ll i=lim;i<=n;++i) {
(ans+=pw*pw2[cnt2]%MOD*pw3[cnt3])%=MOD;
pw=pw*x%MOD; inc();
}
printf("%lld\n",(ans-1+MOD)%MOD);
return 0;
}
const int S=1<<21;
char p0[S],*p1,*p2;
#define getchar() (p2==p1&&(p2=(p1=p0)+fread(p0,1,S,stdin))==p1?EOF:*p1++)
inline int read() {
static int x,c,f;x=0;f=1;
do c=getchar(),c=='-'&&(f=-1); while(!isdigit(c));
do x=x*10+(c&15),c=getchar(); while(isdigit(c));
return x*f;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】