[海军国际项目办公室]乘法

乘法

题目概述

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

题解

显然,我们去掉末尾 0 0 0保留的最后 16 16 16位,转化成二进制位后末尾的 0 0 0不可能超过 3 3 3个,我们可以将原来 n ! n! n!中的 0 0 0全部去掉,最后在求出 0 0 0的个数,模 4 4 4后乘上。
既然,我们已经去掉所有的 2 2 2的倍数了,那么我们乘的肯定都是奇数,我们可以按照原数中含 2 2 2的个数分类,定义 f n = ∏ i ∈ [ 1 , n ] , 2 ∤ i i f_{n}=\prod_{i\in[1,n],2 \nmid i}i fn=i[1,n],2ii,显然有
A n s = ∏ i = 0 log ⁡   n f n 2 i Ans=\prod_{i=0}^{\log\,n}f_{\frac{n}{2^i}} Ans=i=0lognf2in
我们考虑 f n f_n fn怎么算,显然我们的所有奇数都是可以用 2 k + 1 ( k ∈ N ) 2k+1(k\in N) 2k+1(kN)的形式表示的,那么我们 f f f的式子可以写成,
f n = ∏ i = 0 ⌊ n − 1 2 ⌋ ( 2 i + 1 ) f_{n}=\prod_{i=0}^{\lfloor\frac{n-1}{2}\rfloor}(2i+1) fn=i=02n1(2i+1)
由于我们只会保留前面的 64 64 64位,我们可以将它看成一个多项式,令 x = 2 x=2 x=2,取模 x 64 x^{64} x64
F F F为该多项式,有,
F ( x ) = ∏ i = 0 ⌊ n − 1 2 ⌋ ( i x + 1 )    ( m o d    x 64 ) f = ∑ i = 0 63 2 i F [ x i ] F(x)=\prod_{i=0}^{ \lfloor\frac{n-1}{2}\rfloor}(ix+1)\,\,(\mod x^{64})\\ f=\sum_{i=0}^{63}2^iF[x^i] F(x)=i=02n1(ix+1)(modx64)f=i=0632iF[xi]
我们不妨记 u p = ⌊ n − 1 2 ⌋ up=\lfloor\frac{n-1}{2}\rfloor up=2n1,记 g ( u p , i ) = ( ∏ i = 0 u p ( i x + 1 ) ) [ x i ] g(up,i)=(\prod_{i=0}^{up}(ix+1))[x^i] g(up,i)=(i=0up(ix+1))[xi]
考虑如何快速地求出我们的 g ( u p , i ) [ i ∈ [ 0 , 63 ] ] g(up,i)[i\in[0,63]] g(up,i)[i[0,63]]
考虑我们上面多项式系数的组合意义, F [ x i ] F[x^i] F[xi]相当于在 [ 1 , u p ] [1,up] [1,up]中选出 i i i个数,将每个方案中选出的数的积求和。
没有重复元素的无序序列的乘积,这就显然是可以背包求出的了。
我们考虑在前面的无序序列的最后加一个数,这数可能与前面的数重复,就需要容斥,枚举重复的元素个数,
g ( x , i ) = 1 i ∑ j = 1 i ( − 1 ) j − 1 g ( x , i − j ) ∑ y ∈ [ 1 , x ] y j g(x,i)=\frac{1}{i}\sum_{j=1}^{i}(-1)^{j-1}g(x,i-j)\sum_{y\in[1,x]}y^j g(x,i)=i1j=1i(1)j1g(x,ij)y[1,x]yj
该重复的数在 [ 1 , x ] [1,x] [1,x]中是什么都有可能,因为序列求的是乘积,所以我们肯定是将重复的数的幂与 g ( x , i − j ) g(x,i-j) g(x,ij)相乘,得到新的序列的乘积。
而不同的序列之间求的是加减,所以是幂和。
关于 ∑ y ∈ [ 1 , x ] y j \sum_{y\in[1,x]}y^j y[1,x]yj的求法,我们在微信步数中讲到过的,可以通过斯特林数反演求出,所以有,
g ( x , i ) = 1 i ∑ j = 1 i ( − 1 ) j − 1 g ( x , i − j ) ∑ k = 1 j k ! { j k } ( x + 1 k + 1 ) g(x,i)=\frac{1}{i}\sum_{j=1}^{i}(-1)^{j-1}g(x,i-j)\sum_{k=1}^{j}k!{j\brace k}\binom{x+1}{k+1} g(x,i)=i1j=1i(1)j1g(x,ij)k=1jk!{kj}(k+1x+1)
后面的 ∑ k = 1 j k ! { j k } ( x + 1 k + 1 ) \sum_{k=1}^{j}k!{j\brace k}\binom{x+1}{k+1} k=1jk!{kj}(k+1x+1),由于只与 x , j x,j x,j相关,我们可以预处理出来,预处理大概是 O ( log ⁡ 2 n   o r   log ⁡ 3 n ) O\left(\log^2n\,or\,\log^3n \right) O(log2norlog3n)
而我们前面的 1 i \frac{1}{i} i1是不大好处理的,我们的模数是 2 64 2^{64} 264次方,我们在这个范围下是不大好求出逆元的。
很显然,对于奇数,一定是与 2 64 2^{64} 264次方互质的,我们可以直接通过欧拉定理求出它的逆元, φ ( 2 64 ) = 2 63 \varphi(2^{64})=2^{63} φ(264)=263,直接求 k 2 63 − 1 m o d    2 64 k^{2^{63}-1}\mod 2^{64} k2631mod264
而对于偶数,由于我们答案是整数,所以除之前的数也一定是偶数,我们可以先将 2 2 2的幂除掉,知道我们的除数变为一个奇数,再乘它的逆元。

C 202044 z x y \color{black}{C}\color{red}{202044zxy} C202044zxy提出在除 2 2 2的幂的同时,由于我们之前取模的是 2 64 2^{64} 264次方,可能会导致超出 2 64 2^{64} 264的部分一回来的时候丢失。
对于 g ( x , i ) g(x,i) g(x,i)由于我们除的是 i i i,算到 f n f_n fn中的时候,会乘上一个 2 i 2^i 2i 2 i 2^i 2i 2 2 2的幂次肯定是大于 i i i 2 2 2的幂次,所以丢失的部分在加入答案时也是一定会被模掉的。
但我们在求组合数时这部分是不可忽略的,这部分不一定能被抵消的(有可能也可以抵消,不过我没去算),我们有两种方法来维护。
一种是直接维护阶乘的序列,显然 ( n m ) = ∏ i = 1 n ( n − i + 1 ) ∏ i = 1 m i \binom{n}{m}=\frac{\prod_{i=1}^{n}(n-i+1)}{\prod_{i=1}^{m}i} (mn)=i=1mii=1n(ni+1),我们可以暴力维护这两项产生的乘的序列,将下面的数与上面它的倍数相匹配,除掉后再乘起来得到组合数,这样的话预处理是 O ( log ⁡ 3 n ) O\left(\log^3n\right) O(log3n)的。
而另一种方法是直接暴力用 __int128 保留 128 128 128位,这样我们往回依旧不会产生冲突了 ,它总不会要移动到128位后吧,这样就是 O ( log ⁡ 2 n ) O\left(\log^2n\right) O(log2n)的。
或者说你也可以猜测一下前面的 2 i 2^i 2i 2 2 2的幂次很多,应该也可以让后面丢失的部分没有影响。

当然,求 g g g的部分也是 O ( log ⁡ 2 n ) O\left(\log^2n\right) O(log2n)的。
前面预处理也是 O ( log ⁡ 2 n ) O\left(\log^2n\right) O(log2n)的。
我们总共要求 log ⁡   n \log\,n logn f f f,所以是 O ( log ⁡ 3 n ) O\left(\log^3n\right) O(log3n)的。
总时间复杂度 O ( T log ⁡ 3 n ) O\left(T\log^3n\right) O(Tlog3n)

源码

我这的代码是猜测不会影响的,也可以过。
PS.可以用占位符 % l l X \%llX %llX输出 16 16 16进制,我考试的时候手动转 16 16 16进制多输出了一个不可见字符,然后就爆零了。

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;       
const int INF=0x3f3f3f3f;       
const int mo=1e9+7;
const int inv2=499122177;
const double jzm=0.997;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
uLL gcd(uLL a,uLL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int T;uLL n,f[70],S[70][70],ans,fac[70],tmp[70],g[70],ff[70],gg[70];
char answer[70];
void init(){
	S[0][0]=fac[0]=1;
	for(int i=1;i<=64;i++)
		for(int j=1;j<=64;j++)
			S[i][j]=S[i-1][j-1]+1ull*j*S[i-1][j];
	for(int i=1;i<=64;i++)fac[i]=1ull*i*fac[i-1];
}
uLL expt(uLL x,int d){return (x>>gg[d])*ff[d];}
uLL sakura(uLL x){
	if(!x)return (uLL)1;uLL up=(x-1uLL)>>1uLL,res=0;
	for(int i=0;i<=63&&i<=up;i++)g[i]=0;
	for(int i=1;i<=63&&i<=up;i++){
		tmp[i]=0;LL pp=up+1uLL;
		for(int j=1;j<=i;j++){
			pp*=up+1uLL-1ull*j;uLL t=1ull*j+1uLL;
			pp=expt(pp,j+1);tmp[i]+=pp*fac[j]*S[i][j];
		}
	}
	g[0]=1;
	for(int i=1;i<=63&&i<=up;i++){
		for(int j=1;j<=i;j++)
			if(j&1)g[i]+=g[i-j]*tmp[j];
			else g[i]-=g[i-j]*tmp[j];
		g[i]=expt(g[i],i);
	}
	for(int i=0;i<=63&&i<=up;i++)res+=(g[i]<<i);
	return res;
}
char Turn(uLL x){
	if(x<10)return x+'0';
	return x-10+'A';
}
signed main(){
	freopen("multiplication.in","r",stdin);
	freopen("multiplication.out","w",stdout);
	read(T);init();
	for(int i=1;i<=64;i++){
		gg[i]=0;ff[i]=1;uLL s=(1uLL<<63)-1uLL,a=i;
		while(!(a&1uLL))gg[i]++,a>>=1;
		if(gg[i])ff[i]=ff[i>>gg[i]];
		else while(s){if(s&1uLL)ff[i]=a*ff[i];a=a*a;s>>=1uLL;}
	}
	while(T--){
		read(n);ans=1;uLL sum=0;
		for(int i=0;i<64;i++)ans*=sakura(n>>i);
		for(int i=1;i<64;i++)sum+=(n>>i);
		sum&=3uLL;ans<<=sum;
		printf("%llX\n",ans);
	}
	return 0;
}

谢谢!!!

posted @ 2021-10-30 16:37  StaroForgin  阅读(12)  评论(0)    收藏  举报  来源