恩欧挨批模拟试题-13

水博客太快乐了

RT

考场

先看 \(T1\) ,哇这不是个大水题!!!
直接快速幂加组合数上。
写一半发现忘了怎么线性求逆元了。。。。
推了半天式子才想起来。。。。(那为什么不用费马小定理呢?
感觉肯定过了。。。

再看 \(T2\) ,没啥思路。。。。
(想到了基环树,但是以为图不连通,是个森林。。。基环树也不好处理。。果断放弃。。。没往下想。。。

再看 \(T3\) ,没啥思路。。。。
式子也不会推。。。。

最后 \(T2\) \(T3\) 糊了两个暴力上去。。。。

分数

预估 : \(t1\) \(100pts\) \(+\) \(t2\) \(30pts\) \(+\) \(t3\) \(40pts\) \(=\) \(170pts\)
实际 : \(t1\) \(80pts\) \(+\) \(t2\) \(20pts\) \(+\) \(t3\) \(30pts\) \(=\) \(130pts\)
发现 \(T1\) 挂了两个点,发现少模 \(+\) \(0\)的逆元忘赋成 \(1\) 。。。

题解

A. 工业题 / a

全场最水的一题,想象一个矩阵,已知最下面的一行与最左边的一列,要求点 \((n,m)\) 的值。
直接算出边上每一个点对最终答案的贡献。
没啥难度。。。放下式子就好了。

\(ans = \sum_{i=1}^{i \le n} f_{i,0} \times a^{m} \times b^{n-i} \times C_{m-1}^{n-i+m-1} + \sum_{i=1}^{i \le m} f_{0,i} \times b^{n} \times a^{m-i} \times C_{n-1}^{m-i+n-1}\)

code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10, mod=998244353;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int inv[N], mul[N], inv1[N];
inline void pre(int n){
	inv[0]=mul[1]=inv[1]=inv1[1]=inv1[0]=1;
	for(int i=2; i<=n; ++i){
		inv1[i]=((-mod/i+mod)*inv1[mod%i])%mod;
		mul[i]=(i*mul[i-1])%mod;
		inv[i]=(inv[i-1]*inv1[i])%mod;
	}
}
inline int qp(int n, int m){
	int ans=1;
	while(m){
		if(m&1) ans*=n, ans%=mod;
		m>>=1; n*=n; n%=mod;
	}
	return ans;
}
int C(int n, int m) { return n<=m ? 1 : (((mul[n]*inv[m])%mod)*inv[n-m])%mod; }
int n, m, a, b;
int f1[N], f2[N], ans;
signed main(void){
	n=read(); m=read(); pre(n+m);
	a=read()%mod, b=read()%mod;
	for(int i=1; i<=n; ++i) f1[i]=read()%mod;
	for(int i=1; i<=m; ++i) f2[i]=read()%mod;
	for(int i=1; i<=n; ++i){
		ans+=(((((f1[i]*qp(a, m))%mod)*qp(b, n-i))%mod)*C(n-i+m-1, m-1))%mod;
		ans%=mod;
	}
	for(int i=1; i<=m; ++i){
		ans+=(((((f2[i]*qp(b, n))%mod)*qp(a, m-i))%mod)*C(m-i+n-1, n-1))%mod;
		ans%=mod;
	}
	printf("%lld\n", ans);
	return 0;
}

B. 卡常题 / b

直接算貌似很麻烦,考虑转化。
计算出点权,将每次给出的两个 \(X\) 点直接连边,就避免了出现 \(Y\) 点,得到了一课基环树。
基环树上每一条边意义为所连接的两个点中,至少选择其中一个。
求选择树上哪些点可以满足上述关系且使点权和最少。

基环树的常见操作,换上随便断一条边 \((u,v)\) 形成一棵树,树上 \(DP\) 即可。
这个树上 \(DP\) 很套路,直接乱搞即可,令 \(f_{u,0}\) 表示不选当前点,子树的最小点权和, \(f_{u,1}\) 表示选当前点,子树的最小点权和,然后再乱搞就可以了
但是因为删除了一条边,直接 \(DP\) 很可能不能满足选择了 \(u, v\) 中的一个,因此以 \(u\)\(v\) 分别为根跑一次 \(DP\) ,再在 \(f_{u,1}\)\(f_{v,1}\) 中取较小值即可。

code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, a, b, ans;
int x, y, u, v;
vector<int> l[N];
int f[N][2], val[N];
void dfs(int u, int fa){
	f[u][1]=val[u]; f[u][0]=0;
	for(int i=0; i<l[u].size(); ++i){
		int v=l[u][i];
		if(v==fa) continue;
		dfs(v, u); f[u][0]+=f[v][1];
		f[u][1]+=min(f[v][1], f[v][0]);
	}
}
int fa[N];
int find(int x) { return fa[x]==x ? x : fa[x]=find(fa[x]); }
int main(void){
	n=read(); a=read(); b=read();
	for(int i=1; i<=n; ++i) fa[i]=i;
	for(int i=1; i<=n; ++i){
		x=read(), y=read();
		val[x]+=a; val[y]+=b;
		if(find(x)==find(y)) { u=x, v=y; continue; }
		l[x].push_back(y); l[y].push_back(x);
		fa[find(x)]=find(y);
	}
	dfs(u, 0); ans=f[u][1];
	dfs(v, 0); ans=min(ans, f[v][1]);
	printf("%d\n", ans);
	return 0;
}

C. 玄学题 / c

这个题解写的异常的清楚。。。。

因此直接把题解 \(Ctrl\) \(C\) \(+\) \(Ctrl\) \(V\) 下来就好了。。。。
主要是因为懒

那个-1 的幂是由 \(d(i*j)\) 的和的奇偶性决定的。
\(d(x)\) 为偶数时并没有任何影响,我们只考虑 \(d(x)\) 为奇数的时候,
不难想到,x 这个时候是完全平方数。
我们把 i 拆成 \(p*q^{2}\)\(p\) 没有平方因子),那 \(j\) 必须有 \(p*r^{2}\)
形式,所以对于每个 \(i\),都有 \(\sqrt{ \frac{m}{p} }\)\(j\) 产生贡献。
至于 \(p\) 的求法,线性筛即可。

code
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+10;
inline long long read(){
	long long f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int pri[N], cnt, mn[N];
bool p[N];
inline void sieze(int n){
	mn[1]=1;
	for(int i=2; i<=n; ++i){
		if(!p[i]) pri[++cnt]=i, mn[i]=i;
		for(int j=1; j<=cnt&&i*pri[j]<=n; ++j){
			p[i*pri[j]]=1; mn[i*pri[j]]=mn[i]*pri[j];
			if(mn[i]%pri[j]==0) mn[i*pri[j]]/=pri[j]*pri[j];
			if(i%pri[j]==0) break;
		}
	}
}
int n, ans;
long long m;
signed main(void){
	ans=n=read(), m=read();	sieze(n+1);
	for(int i=1; i<=n; ++i){
		int ul=sqrt(m/mn[i]);
		ans-=(ul&1)<<1;
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2021-07-13 15:52  Cyber_Tree  阅读(32)  评论(0编辑  收藏  举报