[SDOI/SXOI2022] 进制转换

一、题目

点此看题

二、解法

怎么根号分治成热门考点了,今年省选遍地走。

本题的难点就是你要同时对两个进制规划,但实际上我们只能在线地考虑其中一个进制,另一个进制只能记录在状态中。暴力的 \(dp\) 方法是:设 \(f[i][j]\) 为从高到低考虑前 \(i\) 个三进制位,对应的二进制是 \(j\),转移暴力枚举当前填什么。

考虑优化这个状态,我们想尽快确定二进制位上的值这样以后就不需要记录了。设 \(l_i\) 表示 \(3^i\) 最多影响后二进制的后 \(i\) 位,那么 \(j\) 的范围就限制在了 \([0,l_{i+1})\) 中,在 \(i\rightarrow i-1\) 的过程中,我们可以把 \([l_i,l_{i+1})\) 这些数位计算了。

但是还可能存在进位问题,我们再记录 \(w\in\{0,1\}\) 表示后面的数位是否会进位给 \(l_i\),由于是给后面提出了要求,所以在 \(dp\) 过程中需要保证它成立。

看似这个优化没有什么用,但是对于前 \(m/2\) 位,搜索带来的情况数只有 \(O(\sqrt n)\);对于后 \(m/2\) 位,状态被限制在了 \(O(\sqrt n)\) 的范围内。所以我们只对后 \(m/2\) 位记忆化,时间复杂度 \(O(\sqrt n)\)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define int long long
const int M = 1<<11;
const int MOD = 998244353;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,x,y,z,px[M][3],py[M],pz[M],f[1<<24][2];
int m,l[M],s[M],bit[M],p[M],w[M];
int qkpow(int a,int b)
{
	int r=1;
	while(b>0)
	{
		if(b&1) r=r*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return r;
}
int dfs(int x,int ns,int nw,int u)
{
	if(x==-1) return nw?0:py[bit[ns]];
	if(!u && x<m/2 && f[ns+s[x]][nw]!=-1)
		return f[ns+s[x]][nw];
	int r=0;
	for(int i=0;i<3;i++) for(int tw=0;tw<2;tw++)
	{
		if(u && i>w[x]) continue;
		int ts=ns+i*p[x]+(tw<<l[x]);
		if((ts>=(1ll<<l[x+1]))!=nw) continue;
		ts&=(1ll<<l[x+1])-1;
		int A=px[x][i],B=py[bit[ts>>l[x]]],C=pz[i];
		r=(r+dfs(x-1,ts&((1ll<<l[x])-1),tw,u&(i==w[x]))
		*A%MOD*B%MOD*C%MOD)%MOD;
	}
	if(!u && x<m/2) f[ns+s[x]][nw]=r;
	return r;
}
signed main()
{
	//freopen("conversion.in","r",stdin);
	//freopen("conversion.out","w",stdout);
	n=read();x=read();y=read();z=read();
	py[0]=pz[0]=p[0]=1;
	for(int i=1;i<(1<<10);i++)
		bit[i]=bit[i>>1]+(i&1);
	for(int i=1;i<=50;i++)
		py[i]=py[i-1]*y%MOD,pz[i]=pz[i-1]*z%MOD;
	while(n) w[m++]=n%3,n/=3;
	for(int i=1;i<=m;i++) p[i]=p[i-1]*3;
	for(int i=0;i<=m;i++)
	{
		px[i][0]=1;px[i][1]=qkpow(x,p[i]);
		px[i][2]=px[i][1]*px[i][1]%MOD;
		while((1ll<<l[i])<=p[i]) l[i]++;
		if(i) s[i]=s[i-1]+(1ll<<l[i]);
	}
	memset(f,-1,sizeof f);
	printf("%lld\n",(dfs(m-1,0,0,1)+MOD-1)%MOD);
}
posted @ 2022-05-20 16:32  C202044zxy  阅读(246)  评论(4编辑  收藏  举报