题解 乘法
好题!
先考虑如何去掉末位的0
在十六进制下末位有0等价于这个数是16的倍数
直接提出所有16不太好写,所以可以先提出所有的2
最终将2的个数模4乘回去即可
于是现在要算 \(n!\) 提出所有2的结果
可以奇偶分治,先算出 \([1, n]\) 中所有奇数的积,从所有偶数中提出一个2,再递归算这些偶数都除掉一个2的乘积
2的个数很好统计,每一层就是 \(\lfloor \frac{n}{2} \rfloor\)
然后考虑怎么算 \([1, n]\) 中奇数之积
即为 \(\prod\limits_{i=1}^{\frac{n-1}{2}}(2i+1)\)
考虑如何在 \(\pmod{2^{64}}\) 的意义下处理这个式子
将括号直接拆开,发现如果有一项的项数 \(\geqslant64\) 则一定不产生贡献
于是们可以枚举项数,将其中2的次幂提出来
则问题转化为「在 \([1, n]\) 的数中任选 \(m\) 个数的所有方案中的 \(m\) 个数之积」
考虑这个东西怎么算
若令 \(g_{i, j}\) 为从 \(i\) 个数中任选 \(j\) 个数的所有方案的乘积之和
有转移
这个东西直接算是 \(O(\text{炸天})\) 的,所以可以插值求,但是我太菜了不会插值
于是有dalao的一种处理方法转化出了斯特林数
但还有另一种理解方式:
我们发现现在是要从 \([1, n]\) 这 \(n\) 个数中选 \(m\) 个产生贡献,剩下的贡献为1
写出来就是 \(\sum_i\prod_{j=1}^n x_j\),这里
就是说 \(j\) 的贡献是 \(j\) 或 1,而要求的是有恰好 \(m\) 个数产生贡献的方案的贡献和
试着构造一下递推式
发现方案数很好算,是 \(f_{i, j}=f_{i-1, j}+f_{i-1, j-1}\)
试着让它能一并计算贡献
这里一共有两类转移,发现第二类转移恰好会做 \(m\) 次
我们需要在转移中乘上数次当前这个数
发现与 \(\begin{bmatrix}i\\ j\end{bmatrix}=\begin{bmatrix}i-1\\ j-1\end{bmatrix}+(i-1)\begin{bmatrix}i-1\\ j\end{bmatrix}\) 类似
发现在 \(\begin{bmatrix}0\\ 0\end{bmatrix}\) 到 \(f\begin{bmatrix}n\\ k\end{bmatrix}\) 的所有转移路径上恰好会有 \(n-k\) 个数乘上它本身
我们想让它乘的是它自身-1,于是我们做到 \(n+1\)
于是我们想求的答案就是所有转移路径的和,即为 \(\begin{bmatrix}n+1\\ n-m+1\end{bmatrix}\)
- 于是形如 \(n\) 个数中选 \(m\) 个求和之类都能类似地构造?那第二类斯特林数的递推式能拿来干嘛
于是式子有了,现在想在log级别求出一个斯特林数
不太可求,但发现 \(n, m\) 的差值很小,于是可以暴力DP
令 \(dp[i][j]\) 为 \(i\) 个数组成 \(j\) 个 \(\geqslant 2\) 的环的方案数
于是 \(dp[i][j] = \sum\limits_{k=2}^{i}\binom{i-1}{k-1}dp[i-k][j-1]\begin{bmatrix}k\\ 1\end{bmatrix}\)
于是可以DP,统计答案就是枚举状态乘上 \(\binom{n}{i}\)
这里组合数的话可以提出所有偶数,奇数可以欧拉定理预处理逆元
复杂度 \(O(Tlog^4n)\) 但跑不满
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define ull unsigned long long
#define int long long
ull n, T; ll m=63;
ull dp[145][145], fac[145], C[145][145], inv[145], ans, cnt;
ull s[1000][1000];
inline ull qpow(ull a, ull b) {ull ans=1; for (; b; a=a*a,b>>=1) if (b&1) ans=ans*a; return ans;}
ull count(ull n) {ull ans=0; while (!(n&1)) ++ans, n>>=1; return ans;}
ull del(ull n) {while (!(n&1)) n>>=1; return n;}
ull C2(ull n, ull k) {
// return !k?1:C[n][k];
// cout<<"C: "<<n<<' '<<k<<endl;
if (!k) return 1;
if (n<k) return 0;
ull cnt=0, ans=1;
for (ull i=0; i<k; ++i) {
ans*=del(n-i);
cnt+=count(n-i);
}
for (ull i=1; i<=k; ++i) {
ans*=inv[del(i)];
cnt-=count(i);
}
return ans*qpow(2, cnt);
}
ull prod(ull n) {
// cout<<"in_prod: "<<n<<endl;
ull ans=0;
#if 0
for (int i=0; i<64; ++i) if (n+1>=i) ans+=(1llu<<i)*s[n+1][n-i+1]; //, cout<<"add: "<<(1llu<<i)*s[n+1][n-i+1]<<endl;
return ans;
#else
for (int m=0; m<64; ++m) if (n+1>=m) {
ull tem=0;
for (int i=m; i<=140; ++i) if (dp[i][i-m]) tem+=C2(n+1, i)*dp[i][i-m]; //, cout<<"i: "<<i<<' '<<C2(n+1, i)<<' '<<dp[i][i-m]<<endl;
ans+=(1llu<<m)*tem;
// cout<<"add: "<<(1llu<<m)*tem<<endl;
}
// cout<<"return: "<<ans<<endl;
return ans;
#endif
}
ull calc(ull n) {
// cout<<"calc: "<<n<<endl;
if (!n) return 1;
ull ans=prod((n-1)/2);
// cout<<"prod: "<<(n-1)/2<<' '<<prod((n-1)/2)<<endl;
cnt=(cnt+n/2)%4;
return ans*calc(n/2);
}
signed main()
{
freopen("multiplication.in", "r", stdin);
freopen("multiplication.out", "w", stdout);
s[0][0]=1;
for (int i=1; i<1000; ++i) for (int j=1; j<=i; ++j) s[i][j]=s[i-1][j-1]+(i-1)*s[i-1][j];
for (int i=0; i<=140; ++i) for (int j=C[i][0]=1; j<=i; ++j) C[i][j]=C[i-1][j-1]+C[i-1][j];
fac[0]=fac[1]=1;
for (int i=2; i<=140; ++i) fac[i]=fac[i-1]*i;
for (int i=1; i<=140; ++i) if (i&1) inv[i]=qpow(i, (1llu<<63)-1);
dp[0][0]=1;
for (int i=1; i<=140; ++i) {
for (int j=1; j<=i; ++j) {
for (int k=2; k<=i; ++k) {
dp[i][j]+=C[i-1][k-1]*dp[i-k][j-1]*fac[k-1];
}
// if (i<=5 && j<=5) printf("dp[%d][%d]=%llu\n", i, j, dp[i][j]);
}
}
cin>>T;
while (T--) {
cin>>n;
cnt=0;
ans=calc(n);
// cout<<"ans: "<<ans<<endl;
// cout<<"cnt: "<<cnt<<endl;
printf("%llX\n", ans*qpow(2, cnt));
// cout<<ans*qpow(2, cnt)<<endl;
}
return 0;
}