当前位置:网站首页>[ACNOI2022]做过也不会

[ACNOI2022]做过也不会

2022-06-24 06:56:00 OneInDark

题目

题目背景
卷爷的刀已经抵在喉头;寒光一闪;我看到鲜血飞溅。我的身体不受控制地坠向地面……

我从现实中惊醒。“幸好在梦世界里不会发生这样的事。” 我长吁一口气。

题目描述
n × n n\times n n×n 矩阵 A A A 的行列式,其中
A i , j = { 1 ( i = j ) 0 ( i ≠ j ∧ i ∣ j ) C ( otherwise ) A_{i,j}=\begin{cases}1 & (i=j)\\0 & (i\ne j\land i\mid j)\\ C & (\text{otherwise}) \end{cases} Ai,j=10C(i=j)(i=jij)(otherwise)

998244353 998244353 998244353 取模。

数据范围与提示
n ⩽ 1 0 11 n\leqslant 10^{11} n1011 。时限 6 s \rm 6s 6s

思路

简单推导 det ⁡ \det det 的方式:下一行减去上一行,变成海森堡矩阵。

复杂方法:将对角线上 1 1 1 视作 C + ( 1 − C ) C+(1-C) C+(1C),枚举哪些位置选择了 ( 1 − C ) (1-C) (1C),则剩余部分是主子式。

引理:当且仅当主子式保留的行列编号 { x i } \{x_i\} { xi} 满足 x 1 ∣ x 2 ∣ x 3 ∣ ⋯ ∣ x k x_1\mid x_2\mid x_3\mid\cdots\mid x_k x1x2x3xk 时,其 d e t \rm det det C k C^k Ck,否则是 0 0 0

证明:每一行都只有一个真后缀为全零,其余是 C C C 。所以必为下三角矩阵,此时两两之间有倍数关系。

□ \square

由此,我们对没选择的行列编号进行 d p \tt dp dp,可以很容易地写出转移
f ( n ) = C ( 1 − C ) n − 1 + ∑ d ∣ n f ( d ) ( 1 − C ) n − d − 1 C f(n)=C(1-C)^{n-1}+\sum_{d\mid n}f(d)(1-C)^{n-d-1}C f(n)=C(1C)n1+dnf(d)(1C)nd1C

以及
a n s = ( 1 − C ) n + ∑ i = 1 n f ( i ) ( 1 − C ) n − i ans=(1-C)^n+\sum_{i=1}^{n}f(i)(1-C)^{n-i} ans=(1C)n+i=1nf(i)(1C)ni

稍微变形一下
f ( n ) ( 1 − C ) − n = C 1 − C + ∑ d ∣ n C 1 − C ( f ( d ) ( 1 − C ) − d ) f(n)(1-C)^{-n}=\frac{C}{1-C}+\sum_{d\mid n}\frac{C}{1-C}(f(d)(1-C)^{-d}) f(n)(1C)n=1CC+dn1CC(f(d)(1C)d)

简记 v : = C 1 − C v:=\frac{C}{1-C} v:=1CC,不妨设 C ≠ 1 C\ne 1 C=1 。并记 g ( n ) : = f ( n ) ( 1 − C ) − n g(n):=f(n)(1-C)^{-n} g(n):=f(n)(1C)n
g ( n ) = v + ∑ d ∣ n d ≠ n v ⋅ g ( d ) (1) g(n)=v+\sum_{d\mid n}^{d\ne n}v\cdot g(d)\tag{1} g(n)=v+dnd=nvg(d)(1)

a n s = ( 1 − C ) n ∑ i = 1 n g ( i ) ans=(1-C)^n\sum_{i=1}^{n}g(i) ans=(1C)ni=1ng(i)

注意到 g ( i ) g(i) g(i) 不是积性函数,我开始脑抽,打算写出关于质因子指数的 g ( i ) g(i) g(i) 的表达式。

脑抽中

n = ∏ p i t i n=\prod p_i^{t_i} n=piti,记
λ l : = ∏ ( l − 1 + t i t i ) \lambda_l:=\prod{l-1+t_i\choose t_i} λl:=(til1+ti)

即单个质因子的指数下降方案,在长为 l l l 的链上。容斥以确保每一步着实有下降。
α l : = ∑ i = 0 l − 1 ( l − 1 i ) ( − 1 ) i λ l − i \alpha_l:=\sum_{i=0}^{l-1}{l-1\choose i}(-1)^i\lambda_{l-i} αl:=i=0l1(il1)(1)iλli

g ( n ) = ∑ i = 1 log ⁡ n α i v i = ∑ i = 1 log ⁡ n v i ∑ j = 0 i − 1 ( i − 1 j ) ( − 1 ) j λ i − j = ∑ i = 1 log ⁡ n v i ∑ j = 0 i − 1 ( i − 1 j ) ( − 1 ) j ∏ ( i − j − 1 + t k t k ) = ∑ l ( ∑ i = l + 1 log ⁡ n v i ( i − 1 i − l − 1 ) ( − 1 ) i − l − 1 ) ∏ ( l + t k t k ) = ∑ l β l ⋅ I l + 1 ( n ) \begin{aligned} g(n)&=\sum_{i=1}^{\log n}\alpha_iv^i=\sum_{i=1}^{\log n}v^i\sum_{j=0}^{i-1}{i-1\choose j}(-1)^j\lambda_{i-j}\\ &=\sum_{i=1}^{\log n}v^i\sum_{j=0}^{i-1}\binom{i-1}{j}(-1)^j\prod{i-j-1+t_k\choose t_k}\\ &=\sum_{l}\left(\sum_{i=l+1}^{\log n}v^i{i-1\choose i-l-1}(-1)^{i-l-1}\right)\prod{l+t_k\choose t_k}\\ &=\sum_{l}\beta_l\cdot I^{l+1}(n) \end{aligned} g(n)=i=1lognαivi=i=1lognvij=0i1(ji1)(1)jλij=i=1lognvij=0i1(ji1)(1)j(tkij1+tk)=l(i=l+1lognvi(il1i1)(1)il1)(tkl+tk)=lβlIl+1(n)

I l + 1 I^{l+1} Il+1 ( l + 1 ) (l{+}1) (l+1) I I I 的狄利克雷卷积的结果。于是可以 O ( log ⁡ n ) \mathcal O(\log n) O(logn) 次筛法解决,比如杜教筛或 min25 \texttt{min25} min25

正解

杜教筛不基于积性函数。而且,“因子的值求和” 不像狄利克雷卷积吗?这不像分治 FTT \textit{FTT} FTT 吗?

事实上, ( 1 ) (1) (1) 式两边同时求前缀和立刻有
⇒ S ( n ) = v n + ∑ i = 2 n v ⋅ S ( ⌊ n / i ⌋ ) \Rightarrow S(n)=vn+\sum_{i=2}^{n}v\cdot S(\lfloor n/i\rfloor) S(n)=vn+i=2nvS(n/i)

其中 S ( n ) = ∑ i ⩽ n g ( i ) S(n)=\sum_{i\leqslant n}g(i) S(n)=ing(i) 。于是杜教筛吧。时间复杂度 O ( n 2 / 3 ) \mathcal O(n^{2/3}) O(n2/3) 的……吗?

注意非积性函数不能做严格意义上的线性筛。需优化。事实上,质因数的指数的可重集相同时, g g g 的值显然相同。因此我们只有很少的值需要枚举因子。但是怎么找到对应关系呢?

一个方法是 hash \texttt{hash} hash 。当然我们也可以魔改线性筛,每个数记录其最大质因子和次数,并预处理每个质数的幂。然后存 f i f_i fi 为,与 i i i 拥有相同的指数可重集的数中,最小的一个。直接求 f i f_i fi 麻烦,可以求 j j j 使得 f i = f j f_i=f_j fi=fj 。设 i i i 移除最大质因子后得到 u u u,若 f u ≠ u f_u\ne u fu=u j = i u f u j=\frac{i}{u}f_u j=uifu,否则再做指数与质因数编号的判断。

最终只有 500 + 500+ 500+ 个数需暴力计算,所以复杂度总算是 O ( n 2 / 3 ) \mathcal O(n^{2/3}) O(n2/3) 了。

代码

#include <cstdio>
#include <algorithm> // Almighty XJX yyds!!
#include <cstring> // oracle: ZXY yydBUS!!!
#include <cctype> // I hate rainybunny.
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
    
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
	for(; isdigit(c); c=getchar()) a = a*10+(c^48);
	return a*f;
}

const int LOGN = 40, MOD = 998244353;
inline llong qkpow(llong b, int q){
    
	llong a = 1;
	for(; q; q>>=1,b=b*b%MOD) if(q&1) a = a*b%MOD;
	return a;
}

const int MAXN = 23544347;
bool isPrime[MAXN]; int* ppow[MAXN];
int primes[MAXN], primes_size, g[MAXN];
int fa[MAXN], big[MAXN], num[MAXN], nxt[MAXN], cnt[MAXN];
void dfs(int &res, int x, int y){
    
	if(x == 1){
     if(g+y != &res){
     res = (res+g[y])%MOD; } return; }
	rep(i,0,num[x]) dfs(res,nxt[x],y), y *= big[x];
}
void sieve(const int &v, int n = MAXN-1){
    
	memset(isPrime+2, true, n-1);
	fa[1] = 1, g[1] = v;
	for(int i=2,&len=primes_size; i<=n; ++i){
    
		if(isPrime[i]){
    
			primes[++len] = big[i] = i, cnt[i] = 1;
			num[i] = 1, fa[i] = 2, nxt[i] = 1;
			g[i] = int(llong(v+1)*v%MOD);
			for(int j=2,p=i; true; ++j,p*=i)
				if(p > n/i){
     ppow[i] = new int[j]; break; }
			for(int j=0,p=1; p<=n/i; ++j,p*=i,ppow[i][j]=p);
		}
		else{
    
			const int rt = fa[nxt[i]];
			if(rt != nxt[i]) g[i] = g[fa[i] = fa[rt*(i/nxt[i])]];
			else if(big[i] != primes[cnt[rt]+1]){
     // not nearest
				fa[i] = rt*ppow[primes[cnt[rt]+1]][num[i]];
				fa[i] = fa[fa[i]], g[i] = g[fa[i]];
			}
			else if(rt != 1 && num[rt] < num[i]){
    
				fa[i] = nxt[rt]*ppow[big[rt]][num[i]]
					*ppow[big[i]][num[rt]]; // switch
				fa[i] = fa[fa[i]], g[i] = g[fa[i]];
			}
			else{
     // I am smallest
				static int wxk = 0; ++ wxk;
				fa[i] = i, dfs(g[i],i,1);
				g[i] = int(llong(g[i]+1)*v%MOD);
			}
		}
		for(int j=1; j<=len; ++j){
    
			const int to = i*primes[j]; if(to > n) break;
			isPrime[to] = false, big[to] = big[i];
			if(!(i%primes[j])){
    
				if(nxt[i] == 1) num[to] = num[i]+1, nxt[to] = 1;
				else num[to] = num[i], nxt[to] = nxt[i]*primes[j];
				cnt[to] = cnt[i]; break;
			}
			num[to] = num[i], nxt[to] = nxt[i]*primes[j], cnt[to] = cnt[i]+1;
		}
	}
}

int res[4650];
int getSum(const llong &n, const int &v, llong x){
    
	if(x < MAXN) return g[x];
	int &w = res[n/x]; if(~w) return w;
	w = int(x%MOD); // easiest
	for(llong l=2,r,val; l!=x+1; l=r){
    
		val = x/l, r = x/val+1; // move
		w = int((w+(r-l)%MOD*getSum(n,v,val))%MOD);
	}
	return w = int(llong(w)*v%MOD);
}

int main(){
    
	llong n; scanf("%lld",&n); int c = readint();
	if(c == 1){
     puts(n <= 2 ? "1" : "0"); return 0; }
	int v = int(c*qkpow(1-c+MOD,MOD-2)%MOD);
	sieve(v); rep0(i,2,MAXN) g[i] = (g[i]+g[i-1])%MOD;
	memset(res, -1, sizeof(res));
	int ans = getSum(n,v,n)+1;
	ans = int(ans*qkpow(1+MOD-c,int(n%(MOD-1)))%MOD);
	printf("%d\n",ans);
	return 0;
}
原网站

版权声明
本文为[OneInDark]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_42101694/article/details/125434341