专项测试 数学1

A. young

要求期望下最小生成树的权值

由于异或,所以考虑二进制拆分,分别考虑每一位的贡献

在当前这一位,肯定是将这一位是 \(1\) 的放在一起,是 \(0\) 的放在一起,然后两块之间再连 \(1\) 条边让他们联通起来

那么设 \(f_{n,m}\) 表示在一共 \(n\) 个点权值范围为 \(0\)\(2^m-1\) 的所有情况最小生成树和

那么根据上面说的可以把贡献拆成两部分

1.块内的连边

2.块之间的连边

第一部分可以递归解决,第二部分需要再搞一个 \(dp\) ,设 \(g_{s,t,m}\) 表示一边 \(s\) 个点另一边 \(t\) 个点,权值范围为 \(0\)\(2^m-1\) 的所有情况的两边之间的最小边权和

那么我们枚举两边的大小,可以得到 \(dp\) 式,一边大小为 \(s\) 另一边大小为 \(n-s\) 设为 \(t\)

\(f_{n,m}= \sum\limits_{s=0}^n\binom{n}{s}(2^{(m-1)\times t}\times f_{s,m-1}+2^{(m-1)\times s}\times f_{t,m-1}+g_{s,t,m-1}+[t\neq 0 \& s\neq 0]2^{(m-1)\times (n+1)})\)

解释一下 \(\binom{n}{s}\) 要选 \(s\) 个点来构成这一位为 \(1\) 或者 \(0\) 的, \(t\) 的意义与之相反

\(2^{(m-1)\times t}\times f_{s,m-1}\) 一共有 \(2^{(m-1)}\) 种取值能选 另一个集合一共 \(t\) 个数,两个集合的取值互不影响所以直接乘上方案数,后面的一个同理

\(g\) 同定义

\([t\neq 0 \& s\neq 0]2^{(m-1)\times (n+1)}\) 当两个集合都存在时中间的连边一定产生 \(2^{m-1}\) 的贡献,再乘上所有点随便选的方案数就行

再看如何求 \(g\) 直接求不好求,于是可以转化一下变成求一边 \(s\) 个点,一边 \(t\) 个点,权值范围 \(0\)\(2^m-1\) ,最小边的边权大于等于 \(k\) 的方案数,把这个设为 \(p_{s,t,m,k}\)

那么 \(g_{s,t,m}=\sum\limits_{k=1}^{2^m-1}p_{s,t,m,k}\)

感性理解一下转化,最小边权为 \(k\) 时同一种方案会在大于等于 \(1\) 到大于等于 \(k\) 各计算一次,正好产生 \(k\) 的贡献

再看如何求 \(p\)

根据第 \(m\) 位的取值 将 \(s,t\) 分别分成 \(s0,s1,t0,t1\) 再递归下去求

如果 \(k\) 的第 \(m\) 位是 \(1\) 那么只能是 \(s1,t0\)\(s0,t1\) 同时存在,直接递归到下一层

如果 \(k\) 的第 \(m\) 位是 \(0\) 如果 \(s0,t0\)\(s1,t1\) 同时存在那么这一位的贡献只能是 \(0\)

需要递归下去解决当然还有乘上组合数的系数

两种情况不同时存在那么这一位的贡献肯定有贡献加上随便选的方案数就行

\(p\) 边界是 \(m=0\)\(k=0\) 贡献一种方案,一边的集合空了贡献 \(2^{s\times m}\) 种方案这块要结合代码一起理解

\(g\) 的边界 \(m=0\) 或者 \(s=0\) 时贡献 \(0\)

\(f\) 的边界 \(m=0\) 或者 \(n\leq1\) 时贡献 \(0\)

Code
#include<bits/stdc++.h>
#define int long long
#define rint signed
#define mod 258280327
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m;
int C[60][60],k2[1010];
unordered_map<int,int>f[60],g[60][60],p[60][60][10];
inline int qpow(int x,int k){
	int res=1,base=x;
	while(k){if(k&1) res=res*base%mod;base=base*base%mod;k>>=1;}
	return res;
}
int P(int s,int t,int m,int k){
	if(s>t) swap(s,t);if(!m) return k==0;if(!s) return k2[t*m];
	if(p[s][t][m].find(k)!=p[s][t][m].end()) return p[s][t][m][k];
	if(k&(1<<(m-1))) return p[s][t][m][k]=P(s,t,m-1,k^(1<<(m-1)))*2%mod;
	int res=0;
	for(int s0=0,s1;s0<=s;s0++) for(int t0=0,t1;t0<=t;t0++){
		s1=s-s0,t1=t-t0;
		if((s1&&t1)||(s0&&t0)) res=(res+C[s][s0]*C[t][t0]%mod*P(s0,t0,m-1,k)%mod*P(s1,t1,m-1,k)%mod)%mod;
	}
	return p[s][t][m][k]=(res+k2[(s+t)*(m-1)+1])%mod;
}
inline int G(int s,int t,int m){
	if(s>t) swap(s,t);if(!m||!s) return 0;
	if(g[s][t].find(m)!=g[s][t].end()) return g[s][t][m];
	int res=0,U=(1<<m)-1;
	for(int k=1;k<=U;k++) res=(res+P(s,t,m,k))%mod;
	return g[s][t][m]=res;
}
inline int F(int n,int m){
	if(!m||n<=1) return 0;
	if(f[n].find(m)!=f[n].end()) return f[n][m];
	int res=0;
	for(int s=0,t;s<=n;s++){
		t=n-s;
		res=(res+C[n][s]*(k2[(m-1)*t]*F(s,m-1)%mod+k2[(m-1)*s]*F(t,m-1)%mod+G(s,t,m-1)+(s!=0&&t!=0)*k2[(m-1)*(n+1)])%mod)%mod;
	}
	return f[n][m]=res;
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	n=read(),m=read();k2[0]=1;for(int i=1;i<=1000;i++) k2[i]=k2[i-1]*2%mod;
	C[0][0]=1;for(int i=1;i<=50;i++){C[i][0]=1;for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;}
	printf("%lld\n",F(n,m)*qpow(k2[n*m],mod-2)%mod);
	return 0;
}

B. Simple

\(f(n)\) 表示长度为 \(n\) 的满足条件的个数

那么要求的就是 \(\sum\limits_{i=1}^ni^2\times f(i)\)

考虑如何求 \(f(n)\) 发现只有当 \(n\) 的循环节不为 \(n\) 时无法产生贡献,对于剩下的串他的所有循环同构串中,只有字典最小的会产生贡献

循环节的长度只能为 \(n\) 的因数,于是可以容斥求

循环 \(d\) 次的串一共 \(10^{\frac{n}{d}}\) 个在利用莫比乌斯函数就行

\(f(n)=\frac{\sum\limits_{d|n}\mu(d)10^{\frac{n}{d}}}{n}\)

答案就变成了 \(\sum\limits_{i=1}^{n}i^2\frac{\sum\limits_{d|i}\mu(d)10^{\frac{i}{d}}}{i}\)

\(\sum\limits_{i=1}^ni\sum\limits_{d|i}\mu(d)10^{\frac{i}{d}}\)

枚举因子变成

\(\sum\limits_{d=1}^nd\mu(d)\sum\limits_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}i10^i\)

前面的用杜教筛卷一个 \(id\) 处理,后面的是一个等差乘等比,错位乘一下求出通项用快速幂解决

Code
#include<bits/stdc++.h>
#define int long long
#define rint signed
#define mod 258280327
#define i2 129140164
#define i81 255091681
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,ans;
rint prime[10000010],mu[10000010],cnt;
bool is[10000010];
int smu[10000010];
unordered_map<int,int>Smu;
inline int qpow(int x,int k){
	int res=1,base=x;
	while(k){if(k&1) res=res*base%mod;base=base*base%mod;k>>=1;}
	return res;
}
inline int s(int x){x%=mod;return x*(x+1)%mod*i2%mod;}
inline int S(int x){return ((9*x%mod-1+mod)%mod*qpow(10,x)%mod+1)*i81%mod*10%mod;}
int getsmu(int n){
	if(n<=10000000) return smu[n];
	if(Smu.find(n)!=Smu.end()) return Smu[n];
	int sum=1;
	for(int l=2,r;l<=n;l=r+1){
		r=n/(n/l);
		sum=(sum-(s(r)-s(l-1)+mod)%mod*getsmu(n/l)%mod+mod)%mod;
	}
	return Smu[n]=sum;
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	mu[1]=1;for(int i=2;i<=10000000;i++){
		if(!is[i]) prime[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&i*prime[j]<=10000000;j++){
			is[i*prime[j]]=1;
			if(i%prime[j]==0) break;
			mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=1;i<=10000000;i++) smu[i]=(smu[i-1]+mu[i]*i%mod+mod)%mod;
	n=read();
	for(int l=1,r;l<=n;l=r+1){
		r=n/(n/l);
		ans=(ans+S(n/l)*(getsmu(r)-getsmu(l-1)+mod)%mod)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}

C. mate

可以直接枚举某一个方向走了几步,然后剩下的可以计算出来

\(u,d,l,r\) 分别表示向上,向下,向左,向右走了几步

答案就是 \(\sum\limits_{u=0}^{n}\frac{n!}{u!d!l!r!}\)

注意判断是否合法

然后发现模数不一定是质数于是用类似 \(exLucas\) 求组合数的思想求阶乘再用 \(CRT\) 合并就行

虽然我是直接把上面的式子化成了三个组合数实现的,但原理一样

posted @ 2022-01-03 21:08  Max_QAQ  阅读(65)  评论(0编辑  收藏  举报