题目链接
题目解法
考虑一个时间复杂度较劣,但好理解一些的做法
首先根据期望的线性性,把问题转化成
∑
i
=
0
n
−
1
E
(
x
=
i
)
\sum\limits_{i=0}^{n-1} E(x=i)
i=0∑n−1E(x=i),这一步比较常用
那么现在就考虑变成数
i
i
i 的概率,下面我们令
N
=
n
−
1
N=n-1
N=n−1
考虑对于每次加操作考虑它的贡献,不难发现贡献一定是
c
1
2
0
+
c
2
2
1
+
.
.
.
+
c
L
+
1
2
L
c_12^0+c_22^1+...+c_{L+1}2^L
c120+c221+...+cL+12L 的形式,其中
c
i
c_i
ci 表示在两次乘
2
2
2 操作间的加操作的个数
那么现在的问题就变成了求
c
1
2
0
+
c
2
2
1
+
.
.
.
+
c
L
+
1
2
L
≤
N
c_12^0+c_22^1+...+c_{L+1}2^L\le N
c120+c221+...+cL+12L≤N 的个数,可以发现概率为
1
2
L
+
∑
c
i
\frac{1}{2^{L+\sum c_i}}
2L+∑ci1
考虑
≤
N
\le N
≤N 的限制不优秀,所以把它转化成
=
=
= 的限制,可以添加一项
c
0
c_0
c0:
c
0
+
c
1
2
0
+
c
2
2
1
+
.
.
.
+
c
L
+
1
2
L
=
N
c_0+c_12^0+c_22^1+...+c_{L+1}2^L=N
c0+c120+c221+...+cL+12L=N 的方案数(同时需要乘上
1
2
∑
i
=
1
L
+
1
c
i
\frac{1}{2^{\sum\limits_{i=1}^{L+1}c_i}}
2i=1∑L+1ci1)的系数,注意
c
0
c_0
c0 不能算在内,因为
c
0
c_0
c0 只是为了凑齐
N
N
N,没有其他的实际意义
考虑把
c
i
c_i
ci 拆分成
2
2
2 进制的形式,那么就可以进行数位
d
p
dp
dp
令
f
i
,
j
f_{i,j}
fi,j 表示到第
i
i
i 位进位为
j
j
j 的方案数,然后枚举这一位为
1
1
1 的数的个数
k
k
k 就可以转移,时间复杂度
O
(
l
o
g
3
n
)
O(log^3n)
O(log3n)
注意到我们需要预处理
g
i
,
j
g_{i,j}
gi,j 表示在第
i
i
i 位和为
j
j
j 的方案数(注意要成
2
−
?
2^{-?}
2−? 的系数),这个直接转移即可,注意特判
c
0
c_0
c0
因为对于每一个
L
L
L 都需要计算,所以时间复杂度
O
(
T
l
o
g
4
n
)
O(Tlog^4n)
O(Tlog4n)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int LIM=70,P=998244353;
int ivpw[LIM];
int f[LIM][LIM];//考虑完了前i位,当前位得到的进位为j的方案数
int g[LIM][LIM],tmp[LIM][LIM];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int qmi(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=1ll*res*a%P;
a=1ll*a*a%P;
}
return res;
}
inline void inc(int &x,int y){ x+=y;if(x>=P) x-=P;}
int calc(int L,int n){
if(n<0) return 0;
int lim=ceil(log2(n))+2;
memset(f,0,sizeof(f));memset(g,0,sizeof(g));
for(int i=0;i<=lim;i++){
int cnt=min(i+1,L+1);
memset(tmp,0,sizeof(tmp));
tmp[0][0]=1;
for(int j=0;j<=cnt;j++) for(int k=0;k<=j;k++) if(tmp[j][k]){
inc(tmp[j+1][k],tmp[j][k]);
if(!j) inc(tmp[j+1][k+1],tmp[j][k]);
else inc(tmp[j+1][k+1],tmp[j][k]*ivpw[i-j+1]%P);
}
for(int j=0;j<=cnt+1;j++) g[i][j]=tmp[cnt+1][j];
}
//c_0+c_12^0+...+c_L2^{L-1}+c_{L+1}2^L=n
f[0][0]=1;
for(int i=0;i<lim;i++) for(int j=0;j<LIM;j++) if(f[i][j]) for(int k=0;k<=L+2;k++){
if(((j+k)&1)!=(n>>i&1)) continue;
inc(f[i+1][(j+k)/2],f[i][j]*g[i][k]%P);
}
return f[lim][0]*qmi(qmi(2,L),P-2)%P;
}
void work(){
int n=read(),ans=0;n--;
for(int i=0;i<60;i++) inc(ans,calc(i,n-(1ll<<i)));
printf("%lld\n",ans);
}
signed main(){
ivpw[0]=qmi(2,P-2);
for(int i=1;i<LIM;i++) ivpw[i]=ivpw[i-1]*ivpw[i-1]%P;
int T=read();
while(T--) work();
fprintf(stderr,"%d ms\n",int64_t(1e3*clock()/CLOCKS_PER_SEC));
return 0;
}