CSP-S模拟22

前言:

哈哈哈,又是一场爆零的模拟赛~~

\(T1:\) 木棍

思路:

机房出现了两种思路:

第一种: 我们不难发现,一共就只有五种情况\(\lbrace 334 \rbrace \lbrace 2233 \rbrace \lbrace 4222 \rbrace \lbrace 442 \rbrace \lbrace 22222 \rbrace\),再看一眼数据范围\(T≤100\),所以我们可以生成所有的排列。(应该是这样吧...)

第二种: 贪心。我们将\(3\)变为\(6\),然后贪心地选\(\lbrace 46 \rbrace \lbrace 226 \rbrace \lbrace 4222 \rbrace \lbrace 442 \rbrace \lbrace 22222 \rbrace\)就可以了。

代码(第二种的,丑):

$code$
#include<iostream>
#define int long long
using namespace std;
int T,n2,n3,n4,ans,ans1;
signed main(){
	ios::sync_with_stdio(false);
	cin>>T;
	while(T--){
		ans=0;
		cin>>n2>>n3>>n4;
		ans1=min(n3/2,n4);
		ans+=ans1;
		n3-=2*ans1;n4-=ans1;
		ans1=min(n2,n4/2);
		ans+=ans1;
		n2-=ans1;n4-=2*ans1;
		ans1=min(n3/2,n2/2);
		ans+=ans1;
		n3-=2*ans1;n2-=2*ans1;
		ans1=min(n2/3,n4);
		ans+=ans1;
		n2-=3*ans1;n4-=ans1;
		ans1=n2/5;
		ans+=ans1;
		cout<<ans<<'\n';
	}
	return 0;
}

\(T2:\)

思路:

我们首先将\(a[]\)数组进行排序,正确性是显然的(这地方没法说,“悠然心会,妙处难于君说”,自己手模一下就差不多了懂了)。然后,我们将会得到一个非常可观的性质:前面能选的后面一定能选。就此,我们可以开始考虑\(dp\)了。设\(dp_{i,j}\)表示左边的前\(i\)个点和右边的前\(a_i\)个点种有\(j\)单点和链。然后酱酱,酿酿,再酱酱酿酿就合并好了(其实是空口不好讲,一会儿代码见~~)。我是拿组合数写的,据说还可以用背包的思想把组合数拆了写,还可以滚动数组,不造,不费。

代码:

$code$
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N=5200,mod=998244353,inv2=499122177;
int n,a[N],fac[N],inv[N],f[N][N];
inline int qpow(int x,int y){
	int res=1;
	while(y){
		if(y&1) res=(res*x)%mod;
		x=(x*x)%mod;
		y>>=1;
	}return res;
}//快速幂 
inline int C(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}//组合数 
signed main(){
	ios::sync_with_stdio(false);
	cin>>n;
	//预处理组合数 
	fac[0]=inv[0]=1;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	inv[n]=qpow(fac[n],mod-2);
	for(int i=n-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
	//排序 
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n);
	//递推方程式 
	int ans=0;f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=a[i-1];j++){
			for(int k=j;k<=a[i];k++){
				if(k-j>a[i]-a[i-1]) break;//记得判边界 不然会TLE 
				f[i][k]=(f[i][k]+f[i-1][j]*C(a[i]-a[i-1],k-j)%mod)%mod;
			  //	 上一个的答案继承下来 * 在左边新加入的a[i]-a[i-1]个点中选出k-j来连接 
			}
		}
		ans=(ans+(f[i][1]-a[i]+mod)%mod)%mod;//要减去两个顶点的链,那是不符合条件的 
		for(int j=0;j<=a[i];j++) f[i][j]=(f[i][j]+f[i][j+1]*(j+1)%mod*j%mod)%mod;//将所有链强制定向合并 
	}
	
	ans=ans*inv2%mod;//因为一个环顺时针逆时针算了两次,所以要除以2 
	cout<<ans%mod;
	return 0;
}

\(T3:\) 传令

思路:

整体思路不难。我们发现直接求解很艰难,那我们不妨二分一下答案,然后\(check\)一下当前答案是否符合题意即可,具体实现细节见代码啊~~

代码:

$code$
#include<iostream>
#include<cmath>
#define int long long
using namespace std;
const int N=2e5+5;
int n,k,u,v,d,num,cnt,head[N],dis[N];
struct node{int to,nxt;}e[N<<1];
inline void add(int u,int v){
	e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
	e[++cnt].to=u;e[cnt].nxt=head[v];head[v]=cnt;
}//建边 
inline void dfs(int x,int fa){
	dis[x]=1;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(y==fa) continue;
		dfs(y,x);
		dis[x]=min(dis[x],dis[y]+1);//求出x到子树内被直接通知的城市的最小距离 
	}
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(y==fa) continue;
		if(dis[x]+dis[y]<=0) continue;//满足条件 
		dis[x]=max(dis[x],dis[y]+1);//求出x到子树内被直接通知的城市的最大距离 
	}
	if(dis[x]>d) dis[x]=-d,num++;//当前点是否必须被选中 
}
inline bool check(int x){
	d=x;num=0;
	dfs(1,0);
	if(dis[1]>=1) num++;
	return num<=k;//是否满足条件 
}
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>k;
	for(int i=1;i<n;i++){
		cin>>u>>v;
		add(u,v);//建边 
	}int l=1,r=n;
	while(l<r){//二分答案取最小 
		int mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}cout<<l<<'\n';
	return 0;
}

\(T4:\) 序列

思路:

虽然它看上去很困难的样子,但是经过机房大佬的激情演讲,我发现它还是很困难。然后,终于,在我不懈的努力下,我选择放弃了这道题。根本就看不懂,去找别的学长博客看去吧~~(不过他们今天好像都还没写??我不造啊)

posted @ 2025-09-16 18:20  晏清玖安  阅读(32)  评论(0)    收藏  举报