各种反演等数学难题

CF997C Sky Full of Stars

n × n n\times n n×n的网格内每个格子染三种颜色,其中至少有一行或一列是同一种颜色的方案数。

转化为求没有一行或一列颜色相同。
直接枚举相同的行列数进行容斥:
= 3 ∑ i = 0 n ( n i ) ( − 1 ) i ∑ j = 0 n ( n j ) ( − 1 ) j 3 ( n − i ) ( n − j ) + 2 ∑ i = 1 n ( n i ) ( − 1 ) i 3 n ( n − i ) ( 3 i − 3 ) − 2 × 3 n 2 = 3 ∑ i = 0 n ( n i ) ( − 1 ) i ( 3 n − i − 1 ) n + 2 ∑ i = 1 n ( n i ) ( − 1 ) i 3 n ( n − i ) ( 3 i − 3 ) − 2 × 3 n 2 \begin{aligned}&=3\sum_{i=0}^n \binom ni(-1)^i \sum_{j=0}^n \binom nj(-1)^j 3^{(n-i)(n-j)} + 2\sum_{i=1}^n \binom ni(-1)^i 3^{n(n-i)}(3^i-3) - 2\times 3^{n^2} \\ &=3 \sum_{i=0}^n \binom ni (-1)^i (3^{n-i}-1)^n+ 2\sum_{i=1}^n \binom ni(-1)^i 3^{n(n-i)}(3^i-3) - 2 \times 3^{n^2}\end{aligned} =3i=0n(in)(1)ij=0n(jn)(1)j3(ni)(nj)+2i=1n(in)(1)i3n(ni)(3i3)2×3n2=3i=0n(in)(1)i(3ni1)n+2i=1n(in)(1)i3n(ni)(3i3)2×3n2
后面的那个式子是因为 i i i j j j有一个为 0 0 0的时候相同的染色行列不一定只有三种方案。

A C   C o d e \mathcal AC \ Code AC Code

#include
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;

int n;
int Pow(int b,LL k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod;return r; }

int main(){
	scanf("%d",&n);
	int ans = 3ll * Pow(3,n*1ll*n) % mod , C = 1;
	rep(i,0,n){
		ans = (ans - 3ll * C * (i&1 ? -1ll : 1ll) * Pow(Pow(3,n-i)-1,n)) % mod;
		if(i) ans = (ans - 2ll * C * (i&1 ? -1ll : 1ll) * Pow(3,1ll*n*(n-i)) % mod * (Pow(3,i) - 3)) % mod;
		C = 1ll * C * (n-i) % mod * Pow(i+1,mod-2) % mod;
	}
	printf("%d\n",(ans+mod)%mod);
}

[AGC035F] Two Histograms

各种反演等数学难题_第1张图片
可以发现一个网格对应多个方案,当且仅当存在 l x = y , k y = x − 1 l_x = y , k_y = x-1 lx=y,ky=x1,这时让 l x = y − 1 , k y = x l_x=y-1,k_y=x lx=y1,ky=x也可以得到同样的网格。
所以我们设 l x = y , k y = x − 1 l_x = y , k_y = x-1 lx=y,ky=x1 k y k_y ky不合法,容斥求出所有的合法方案即为本质不同的网格方案(即我们只在所有可以有多个方案的网格只计算 l x = y − 1 , k y = x l_x=y-1,k_y=x lx=y1,ky=x这一种情况。)。
a n s = ∑ i = 0 min ⁡ ( n , m ) ( n i ) ( − 1 ) i ( m i ) i ! ( m + 1 ) n − i ( n + 1 ) m − i ans = \sum_{i=0}^{\min(n,m)} \binom ni (-1)^i\binom mi i! (m+1)^{n-i}(n+1)^{m-i} ans=i=0min(n,m)(in)(1)i(im)i!(m+1)ni(n+1)mi

A C   C o d e \mathcal AC \ Code AC Code

#include
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;

int n,m;
int Pow(int b,LL k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod;return r; }

int main(){
	scanf("%d%d",&n,&m);
	int Cn=1,Cm=1,ans=0,fc=1;
	rep(i,0,min(n,m)){
		ans = (ans + 1ll * Cn * Cm % mod * (i&1 ? -1ll : 1ll) * fc % mod * Pow(m+1,n-i) % mod * Pow(n+1,m-i)) % mod;
		Cn = 1ll * Cn * (n-i) % mod * Pow(i+1 , mod-2) % mod;
		Cm = 1ll * Cm * (m-i) % mod * Pow(i+1 , mod-2) % mod;	
		fc = 1ll * fc * (i+1) % mod;
	}
	printf("%d\n",(ans+mod)%mod);
}

LOJ #6181. 某个套路求和题

f ( n ) f(n) f(n)在质数处为 − 1 -1 1,有平方因子数处为 0 0 0,其他时候为 1 1 1
所以答案等于 ∑ i = 1 μ ( i ) ⌊ n i 2 ⌋ − 2 π ( n ) \sum_{i=1}\mu(i) \left\lfloor\frac {n}{i^2}\right\rfloor-2 \pi(n) i=1μ(i)i2n2π(n)
前者直接算,后者 M i n 2 5 Min_25 Min25
写了一个很麻烦的实现:

#include
#define maxn 200005
#define LL long long
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--) 
using namespace std;

LL n,sn,a[maxn],s[maxn],mu[maxn];
double inv[maxn];
int cnt;
int ID(LL a){ return a <= sn ? a : cnt - n/a + 1; }

int main(){
	scanf("%lld",&n);
	sn = sqrt(n);
	for(LL i=1;i<=n;i++) a[++cnt] = (i = n / (n / i)) , s[cnt] = i - 1;
	for(LL i=2;i<=sn;i++){
		inv[i] = 1.0 / i;
		if(s[i] ^ s[i-1]) for(LL j=cnt,LIM=i*i;a[j]>=LIM;j--)
			s[j] -= s[ID((LL)(a[j] * inv[i] + 1e-9))] - s[i-1];	
	}
	for(int i=1;i<=cnt;i++) mu[i] = -s[i];
	for(LL i=sn;i>=2;i--) if(s[i] ^ s[i-1]) for(LL j=cnt,LIM=i*i;a[j]>=LIM;j--)
		mu[j] -= mu[ID((LL)(a[j] * inv[i] + 1e-9))] + s[i];
	for(int i=sn;i>=1;i--) mu[i] -= mu[i-1];
	mu[1] = 1;
	LL ans = - 2 * s[cnt];
	for(int i=1;i<=sn;i++) ans += mu[i] * (n / i / i);
	printf("%lld\n",(ans%mod+mod)%mod);
}

#528. 「LibreOJ β Round #4」求和

∑ i = 1 n ∑ j = 1 m μ 2 ( gcd ⁡ ( i , j ) ) = ∑ d = 1 min ⁡ ( n , m ) μ 2 ( d ) ∑ i = 1 n d ∑ j = 1 m d [ gcd ⁡ ( i , j ) = 1 ] = ∑ d = 1 min ⁡ ( n , m ) μ 2 ( d ) ∑ p = 1 min ⁡ ( n , m ) d μ ( p ) ⌊ n d p ⌋ ⌊ m d p ⌋ = ∑ D = 1 min ⁡ n , m ⌊ n d ⌋ ⌊ m d ⌋ ∑ d ∣ D μ 2 ( d ) μ ( D d ) \begin{aligned}&\sum_{i=1}^n \sum_{j=1}^m \mu^2(\gcd(i,j)) = \sum_{d=1}^{\min(n,m)} \mu^2(d)\sum_{i=1}^{\frac nd}\sum_{j=1}^{\frac md} [\gcd(i,j)=1]\\&=\sum_{d=1}^{\min(n,m)} \mu^2(d)\sum_{p=1}^{\frac {\min(n,m)}{d}} \mu(p)\lfloor \frac {n}{dp}\rfloor\lfloor \frac {m}{dp} \rfloor \\&=\sum_{D=1}^{\min n,m} \lfloor\frac nd\rfloor\lfloor\frac md\rfloor\sum_{d|D} \mu^2(d)\mu(\frac Dd)\end{aligned} i=1nj=1mμ2(gcd(i,j))=d=1min(n,m)μ2(d)i=1dnj=1dm[gcd(i,j)=1]=d=1min(n,m)μ2(d)p=1dmin(n,m)μ(p)dpndpm=D=1minn,mdndmdDμ2(d)μ(dD)

考虑 ∑ d ∣ D μ 2 ( d ) μ ( D d ) \sum_{d|D} \mu^2(d)\mu(\frac Dd) dDμ2(d)μ(dD),这是个积性函数,我们考虑质数的次方处的值,可以发现这个函数在 p 1 , p 3 , p 4 . . . p c , c ≠ 2 p^1,p^3,p^4...p^c,c\neq 2 p1,p3,p4...pc,c=2处都为 0 0 0
而在 p 2 p^2 p2处为 μ ( p ) \mu(p) μ(p),所以 ∑ d ∣ D μ 2 ( d ) μ ( D d ) = [ x 2 = D ] μ ( x ) \sum_{d|D} \mu^2(d)\mu(\frac Dd) = [x^2 = D]\mu(x) dDμ2(d)μ(dD)=[x2=D]μ(x)其中 x = ⌊ D ⌋ x = \lfloor\sqrt D \rfloor x=D
所以预处理 ≤ n \leq \sqrt n n μ \mu μ的前缀和即可整除优化。

A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 5000007
#define LL long long
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--) 
using namespace std;

LL n,m;
int mu[maxn],pr[maxn],vis[maxn],cnt_pr;

int main(){
	scanf("%lld%lld",&n,&m);
	if(n > m) swap(n,m);
	mu[1]=1;
	int sn = sqrt(n);
	rep(i,2,sn){
		if(!vis[i]) pr[cnt_pr++] = i , mu[i] = -1;
		for(int j=0;pr[j] * i <= sn;j++){
			vis[i * pr[j]] = 1;
			if(i % pr[j] == 0){
				mu[i * pr[j]] = 0;
				break;
			}
			mu[i * pr[j]] = -mu[i];
		}
		mu[i] += mu[i-1];
	}
	int ans = 0;
	for(LL i=1,nxt;i<=n;i=nxt+1){
		nxt = min(n / (n / i) , m / (m / i));
		ans = (ans + 1ll * (n / i) % mod * (m / i % mod) % mod * (mu[(int)(sqrt(nxt)+1e-9)] - mu[(int)(sqrt(i-1)+1e-9)])) % mod;
	}
	printf("%d\n",(ans+mod)%mod);
}

LOJ #6244. 七选五

考虑一个固定的排列 a a a,大小为 r r r,现在你要选出 n n n r r r排列,求这个排列和 a a a中恰好有 x x x相等的方案数。

广义容斥原理模板题:
广义容斥原理:
β ( x ) \beta (x) β(x)为钦定 x x x个条件满足的方案数。
则恰好有 x x x个方案满足的方案数 f ( x ) = ∑ i = x n ( − 1 ) i − x β ( i ) ( i x ) f(x) = \sum_{i=x}^n (-1)^{i-x}\beta(i)\binom {i}{x} f(x)=i=xn(1)ixβ(i)(xi)
容斥系数为 ( − 1 ) i − x ( i x ) (-1)^{i-x}\binom ix (1)ix(xi)是因为各种反演等数学难题_第2张图片
所以对于这题答案 = ∑ i = x r ( i x ) ( − 1 ) i ( r i ) ( n − i r − i ) ( r − i ) ! =\sum_{i=x}^r\binom ix(-1)^i\binom ri \binom {n-i}{r-i}(r-i)! =i=xr(xi)(1)i(ir)(rini)(ri)!

A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 1000007
#define LL long long
#define mod 1000000007
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--) 
using namespace std;

int n,k,x;
int fac[maxn],inv[maxn],invf[maxn];
int C(int n,int m){ return fac[n] * 1ll * invf[m] % mod * invf[n-m] % mod; }

int main(){
	fac[0] = fac[1] = inv[0] = inv[1]= invf[0] = invf[1] = 1;
	scanf("%d%d%d",&n,&k,&x);
	rep(i,2,n) fac[i] = 1ll * fac[i-1] *i  % mod, inv[i] = 1ll * (mod - mod /i) * inv[mod % i] % mod,
		invf[i] = 1ll * invf[i-1] * inv[i] % mod;
	int ans = 0;
	rep(i,x,k)
		ans = (ans + (i-x&1?-1ll:1ll)*C(i,x)%mod*C(k,i)%mod*fac[n-i]%mod*invf[n-k])%mod;
	printf("%d\n",(ans+mod)%mod);
}

[ARC101C] Ribbons on Tree

各种反演等数学难题_第3张图片
f i , j f_{i,j} fi,j表示以 i i i为根的子树内剩下 j j j个点未匹配的容斥系数和。
则每次可以强行让 i i i到父亲的边不被涂色也就是强行匹配剩下的所有点,并乘上一个 − 1 -1 1的容斥系数。

A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 5005
#define LL long long
#define iv2 500000004
#define mod 1000000007
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--) 
using namespace std;

int n,f[maxn][maxn],sz[maxn],fac[maxn],pw[maxn],invf[maxn],inv[maxn];
vector<int>G[maxn];
int C2(int x){ return 1ll * x * (x-1) / 2 % mod; }

void dfs(int u,int ff){
	int v;f[u][1] = sz[u] = 1;
	rep(i,0,G[u].size()-1) if((v=G[u][i])^ff){
		dfs(v,u);
		static int g[maxn]={};
		rep(j,0,sz[u]+sz[v]) g[j] = 0;
		rep(j,0,sz[u]) rep(k,0,sz[v]) g[j+k] = (g[j+k] + 1ll * f[u][j] * f[v][k]) % mod;
		rep(j,0,sz[u]+sz[v]) f[u][j] = g[j];
		sz[u] += sz[v];
	}
	rep(j,1,sz[u]) if(j%2==0)
		f[u][0] = (f[u][0] + f[u][j] * 1ll * fac[j] % mod * pw[j/2] % mod * invf[j/2]) % mod;
	if(ff) f[u][0] = -f[u][0];
}

int main(){
	scanf("%d",&n);
	rep(i,1,n-1){
		int u,v;scanf("%d%d",&u,&v);
		G[u].push_back(v),G[v].push_back(u);
	}
	fac[0] = fac[1] = pw[0] = 1 , pw[1] = iv2;
	inv[0] = inv[1] = invf[0] = invf[1] = 1;
	rep(i,2,n) fac[i] = 1ll * fac[i-1] * i % mod , pw[i] = 1ll * iv2 * pw[i-1] % mod,inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,
		invf[i] = 1ll * invf[i-1] * inv[i] % mod;
	dfs(1,0);
	printf("%d\n",f[1][0]);
}

LOJ 「美团 CodeM 初赛 Round A」二分图染色

以讹传讹真离谱
将问题转化为棋盘模型,则是同颜色的棋子不能在同一行或同一列,不同颜色的棋子不能在同一个格子里。
f n f_n fn为在 n × n n \times n n×n的棋盘内放置一种颜色的棋子的方案数。
则不同颜色的棋子不能在同一个格子里这个条件可以得到容斥一下 a n s = ∑ i = 0 ( − 1 ) i ( n i ) 2 i ! f n − i 2 ans = \sum_{i=0} (-1)^i \binom ni^2i!f_{n-i}^2 ans=i=0(1)i(in)2i!fni2
对于 f n f_n fn有递推式 f n = 2 n f n − 1 − ( n − 1 ) 2 f n − 2 f_n = 2nf_{n-1}-(n-1)^2f_{n-2} fn=2nfn1(n1)2fn2
这部分的证明我看到的网上的题解都是错的。
我们可以在 1... n − 1 1...n-1 1...n1选择一列 x x x,然后在 n n n x x x列(下面简称 ( n , x ) (n,x) (n,x))放置一个棋子,同时将之前 f n − 1 f_{n-1} fn1中对于 n − 1 n-1 n1 n − 1 n-1 n1列的方案作出 x . . . n − 1 x...n-1 x...n1列平移到 x + 1... n x+1...n x+1...n列的操作,这样我们就得到了一个合法方案,同样我们也可以在 1... n − 1 1...n-1 1...n1选择一行 x x x然后在 ( x , n ) (x,n) (x,n)等做类似的操作,也可以直接在 ( n , n ) (n,n) (n,n)放置一个棋子,同时也可以不放,一共 2 n 2n 2n种操作。
但是发现选择一列和选择一行这两种操作构造出来的棋盘可能会重,如果重了一定是因为 n n n行上有一个棋子 n n n列上也有一个棋子,并且这两个棋子都不在 ( n , n ) (n,n) (n,n),所以我们就求出 n n n行上有一个棋子, n n n列上有一个棋子的方案数也即 ( n − 1 ) 2 f n − 2 (n-1)^2f_{n-2} (n1)2fn2,然后减去即可。

A C   C o d e \mathcal AC \ Code AC Code

#include
#define mod 1000000007
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define maxn 10000007
using namespace std;

int n,inv[maxn],f[maxn];

int main(){
	scanf("%d",&n);
	inv[0] = inv[1] = f[0] = 1 , f[1] = 2;
	rep(i,2,n) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod , 
		f[i] = (2ll * i * f[i-1] - (i-1ll) * (i-1ll) % mod * f[i-2]) % mod;
	int ans = 0 , C = 1;
	rep(i,0,n)
		ans = (ans + (i&1?-1ll:1ll) * C * f[n-i] % mod * f[n-i]) % mod,
		C = 1ll * C * (n-i) % mod * (n-i) % mod * inv[i+1] % mod;
	printf("%d\n",(ans+mod)%mod);
}

LOJ #572. 「LibreOJ Round #11」Misaka Network 与求和

∑ i = 1 N ∑ j = 1 N f ( gcd ⁡ ( i , j ) ) k   m o d   2 32 \sum_{i=1}^{N} \sum_{j=1}^{N} f(\operatorname{gcd}(i, j))^{k} \bmod 2^{32} i=1Nj=1Nf(gcd(i,j))kmod232
g ( x ) = f ( x ) k g(x) = f(x)^k g(x)=f(x)k
∑ i = 1 n ∑ j = 1 n g ( gcd ⁡ ( i , j ) ) = ∑ d = 1 n g ( d ) ∑ i = 1 n d ∑ j = 1 n d [ ( i , j ) = 1 ] \begin{aligned}&\sum_{i=1}^n\sum_{j=1}^n g(\gcd(i,j))=\sum_{d=1}^n g(d)\sum_{i=1}^\frac nd \sum_{j=1}^{\frac nd} [(i,j)=1]\end{aligned} i=1nj=1ng(gcd(i,j))=d=1ng(d)i=1dnj=1dn[(i,j)=1]
因为 g g g的定义是次大质因子的 K K K次方,次大质因子一定是 m i n 2 5 min_25 min25中我们筛到的质因子(不像什么次小质因子 m i n 2 5 min_25 min25筛过程中筛不到需要预处理恶心人,比如51nod 1847 奇怪的数学题),所以可以直接筛,后面就是一个 ϕ \phi ϕ的前缀和,具体来说他等于 2 ∑ i = 1 n d ϕ ( i ) − ⌊ n d ⌋ 2\sum_{i=1}^{\frac nd} \phi(i) - \lfloor\frac nd\rfloor 2i=1dnϕ(i)dn

用递推版的 M i n 2 5 Min_25 Min25成功水到 r k 1 rk1 rk1
A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

int n,K,sn,cnt,s[maxn],s1[maxn],a[maxn],g[maxn],phi[maxn];
int ID(int a){ return a <= sn ? a : cnt - n/a + 1; }
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=b*b)if(k&1)r=r*b;return r; }
double inv[maxn];

int main(){
	scanf("%d%d",&n,&K);
	sn = sqrt(n);
	for(int i=1;i<=n;i++) a[++cnt] = (i = (n / (n / i))) , 
		s[cnt] = i - 1 , s1[cnt] = 1ll * i * (i+1) / 2 - 1;
	for(int i=2;i<=sn;i++){
		inv[i] = 1.0 / i;
		if(s[i] ^ s[i-1]) for(int j=cnt,LIM=i*i;a[j]>=LIM;j--){
			int t = ID((int)(a[j] * inv[i] + 1e-9));
			s[j] -= s[t] - s[i-1],
			s1[j] -= (s1[t] - s1[i-1]) * i;
		}
	}
	for(int i=1;i<=cnt;i++) phi[i] = s1[i] - s[i];
	for(int i=sn;i>=1;i--) if(s[i] ^ s[i-1]){
		int pw = Pow(i,K);
		for(int j=cnt,LIM=i*i;a[j]>=LIM;j--){
			double iv = inv[i];
			for(int k=i,f=i-1;1ll*k*i <= a[j];k*=i,f*=i,iv/=i){
				int t = ID((int)(a[j] * iv + 1e-9));
				g[j] += pw * (s[t] - s[i-1]) + g[t];
				phi[j] += f * (phi[t] - s1[i] + s[i] + i);
			}
		}
	}
	for(int i=1;i<=cnt;i++) phi[i] ++ , g[i] += s[i];
	int ans = 0;
	for(int i=1,nxt;i<=n;i=nxt+1){
		nxt = n/(n/i);
		ans += (g[ID(nxt)] - g[ID(i-1)]) * (2 * (phi[ID(n/i)] - 1) + 1);
	}
	unsigned int ret = ans;
	printf("%u\n",ret);
}

你可能感兴趣的:(各种反演等数学难题)