2025 -- 云智计划 -- 【CSP-S】模拟赛 #20&21&23_总结+题解

模拟赛 #20

总结

T1,T2

很快就有了思路。并且打出了代码。没什么问题

题解

T1

不难发觉,他们的最小贡献一定是。两个横坐标之差乘上作用标志之差再加一。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=2e5+5;
signed main()
{
	freopen("table.in","r",stdin);
	freopen("table.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		int x1,x2,y1,y2;cin>>x1>>y1>>x2>>y2;
		cout<<(x2-x1)*(y2-y1)+1<<endl;
	}
	return 0;
}

T2

不难发觉,每一个分出来的量,我们是可以提前预知的。那么我们把每一行所对应的分割线以及每一列所对应的分割线求出来之后再对。这个分割线去做一个判断即可,如果可以,那么就输出该解,如果不可以,则输出无解。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const long long maxn=3000+5;
int a[maxn][maxn],b[maxn],cc[maxn], tar;
void check(vector<int> vr,vector<int> vc)
{
	for(int i=1;i<vr.size();i++)
	{
		for(int j=1;j<vc.size();j++)
		{
			int x2=vr[i],y2=vc[j];
			int x1=vr[i-1]+1,y1=vc[j-1]+1;
			if(a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1]!=tar)
			{
				cout<<"NO";	
				return;
			}
		}
	}
	cout<<"YES"<<endl;
	for(int i=1;i<vr.size()-1;i++) cout<<vr[i]<<' ';
	cout<<endl;
	for(int i=1;i<vc.size()-1;i++) cout<<vc[i]<<' ';
}
signed main()
{
	freopen("land.in","r",stdin);
	freopen("land.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,m,r,c,cnt=0;cin>>n>>m>>r>>c;
	r++,c++;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			char c;cin>>c;
			a[i][j]=c-'0';
			b[i]+=a[i][j],cc[j]+=a[i][j];
			cnt+=a[i][j];
			a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+a[i][j];
		}
	if(cnt%(r*c))
	{
		cout<<"NO";
		return 0;
	}
	int t=cnt/r,sum=0,cntr=0,cntc=0;
	vector<int> vr,vc;
	vr.push_back(0);
	vc.push_back(0);
	for(int i=1;i<=n;i++)
	{
		sum+=b[i];
		if(sum==t) vr.push_back(i),sum=0,cntr++;
	}
	if(cntr!=r)
	{
		cout<<"NO";
		return 0;
	}
	t=cnt/c,sum=0;
	for(int i=1;i<=m;i++)
	{
		sum+=cc[i];
		if(sum==t) vc.push_back(i),sum=0,cntc++;
	}
	if(cntc!=c)
	{
		cout<<"NO";
		return 0;
	}
	tar=cnt/c/r;
	check(vr,vc);
	return 0;
}

模拟赛 #21

总结

T1,T2,T3

很快就有了思路并打出了代码,没有什么大问题。

T4

没有思路,还有一个非常巧妙的做法。

题解

T1

可以发觉对答案产生贡献无非就是两种。

  1. 新加其中一个和原来就有的产生了联系。
  2. 两个新加的产生联系。

对于第一种直接枚举最大值,对于第2种,我们去枚举那些有联系的即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const long long maxn=2e5+5;
map<pair<int,int>,int> mp;
int cnt[maxn],vis[maxn];
signed main()
{
	freopen("Social.in","r",stdin);
	freopen("Social.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,m,k,ans=0,sum=0;cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
	{
		int x;cin>>x;
		vis[x]=1;
	}
	for(int i=1;i<=m;i++)
	{
		int u,v;cin>>u>>v;
		if(u>v) swap(u,v);
		if(u==v&&!vis[u]) cnt[u]++;
		else if(u==v) sum++;
		else
		{
			if(vis[u]&&vis[v]) sum++;
			else if(vis[u]) cnt[v]++;
			else if(vis[v]) cnt[u]++;
			else mp[{u,v}]++;
		}
	}
	int maxx1=0,maxx2=0;
	for(int i=1;i<=k;i++)
	{
		if(maxx1<=cnt[i]) maxx2=maxx1,maxx1=cnt[i];
		else if(maxx2<cnt[i]) maxx2=cnt[i];
	}
	ans=maxx1+maxx2;
	for(auto [x,y]:mp)
	{
		if(x.first!=x.second) ans=max(ans,cnt[x.first]+cnt[x.second]+y);
		else ans=max(ans,cnt[x.first]+y);
	}
	cout<<ans+sum;
	return 0;
}

T2

不难发现,如果说他度为一,那么答案只能是次大值。如果说他度不大于一,则一定可以是最大值,两者比个最大值即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=2e5+5;
int du[maxn],a[maxn];
signed main()
{
	freopen("Melon.in","r",stdin);
	freopen("Melon.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++)
	{
		int u,v;cin>>u>>v;
		du[u]++,du[v]++;
	}
	int ans1=0,ans2=0,ans3=0;
	for(int i=1;i<=n;i++)
	{
		if(du[i]>1) ans1=max(ans1,a[i]);
		else if(ans2<=a[i]) ans3=ans2,ans2=a[i];
		else if(ans3<a[i]) ans3=a[i];
	}
	cout<<max(ans1,ans3);
	return 0;
}

T3

如果我们将偶数位或者奇数位全部翻转(二不管他)。那么此时就会变成是一个零一个一配对,我们只需要对零和一中用较大值减去较小值,然后再用二填一下差即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=2e5+5;
signed main()
{
	freopen("Gift.in","r",stdin);
	freopen("Gift.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		string s;cin>>s;
		int n=s.size(),cnt[3]={},ans=0;s=' '+s;
		for(int i=1;i<=n;i++)
		{
			int x=s[i]-'0';
			if(x!=2&&i%2) x^=1;
			cnt[x]++;
		}
		if(cnt[0]>cnt[1]) swap(cnt[0],cnt[1]);
		if(cnt[0]+cnt[2]<=cnt[1]) ans=cnt[1]-cnt[0]-cnt[2];
		else ans=(cnt[0]+cnt[2]-cnt[1])%2;
		cout<<ans<<endl;
	}
	return 0;
}

T4

不难发觉,这道题我们需要将它的外面比他小的全部给他算上,那么这个时候我们只需要在不考虑他子树时,其他点的总的拓扑序,然后再加上它子树内的拓扑序即可。

具体的我们可以发现。如果说我们要考虑一棵树中的点,他要插在剩下的一些位置上。即 \(C_{n-y}^{s_u-1}\)

再给它乘上拓扑即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=2005+5,mod=998244353;
int n,g[maxn][maxn],inv[maxn],fac[maxn],p[maxn],invp[maxn],fa[maxn],dep[maxn],siz[maxn],pre[maxn];
vector<int> G[maxn];
int kasumi(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
int c(int n,int m)
{
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void dfs(int x)
{
	dep[x]=dep[fa[x]]+1;
	siz[x]=p[x]=1;
	for(auto to:G[x])
	{
		dfs(to);
		siz[x]+=siz[to];
		p[x]=p[x]*p[to]%mod;
	}
	p[x]=p[x]*siz[x]%mod;
	invp[x]=kasumi(p[x],mod-2);
}
void dp(int u)
{
	pre[dep[u]-1]=0;
	for(int v:G[u])
	{
		int tmp=fac[siz[u]-siz[v]]%mod*invp[u]%mod*p[v]%mod*siz[u]%mod*kasumi(siz[u]-siz[v],mod-2)%mod;
		for(int x=dep[u];x<=n-siz[u]+1;x++)
			pre[x]=(pre[x-1]+c(n-siz[v]-x,siz[u]-siz[v]-1)*tmp%mod*g[u][x]%mod)%mod;
		for(int x=n-siz[u]+2;x<=n-siz[v]+1;x++) pre[x]=pre[x-1];
		for(int y=dep[v];y<=n-siz[v]+1;y++)
			g[v][y]=pre[y-1];
		dp(v);
	}
}
signed main()
{
	freopen("Topo.in","r",stdin);
	freopen("Topo.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n;
	for(int i=2;i<=n;i++) cin>>fa[i],G[fa[i]].push_back(i);
	fac[0]=inv[0]=1;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod,inv[i]=kasumi(fac[i],mod-2);
	dfs(1);
	g[1][1]=1;
	dp(1);
	for(int i=1;i<=n;i++)
	{
		int ans=g[i][i]*c(n-i,siz[i]-1)%mod*fac[siz[i]]%mod*invp[i]%mod;
		cout<<ans<<' ';
	}
	return 0;
}

模拟赛 #23

总结

T1,T2

很快就有思路并打出代码,没什么问题。

题解

T1

显然我可以用,前面是y,后面是X,最后用y除以二的安慰奖来解决。我们只需要看一下我们按照这样子的顺序去判断是否可以即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const long long maxn=2e5+5;
signed main()
{
	freopen("prize.in","r",stdin);
	freopen("prize.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		int n,x,y,m;cin>>n>>x>>y>>m;
		if((__int128)n*y<m) cout<<"No"<<endl;
		else
		{
			int d=n*y-m;
			if(d<(y+1)/2)
			{
				if(d%(y-x)||d/(y-x)>n) cout<<"No"<<endl;
				else cout<<"Yes"<<endl;
			}
			else cout<<"Yes"<<endl;
		}
	}
	return 0;
}

T2

我们会发现,如果不考虑重复的情况下,每一个机器人刷的长度一定是一样的。

我们可以将每一次运动完之后,机器人刷的区间加减值给他求出来。

对于两个相邻的机器人,如果说他们俩之间的间距大于了他们所加减值之和,那么就不管它,否则我们就看到最后一次他们俩家简直加起来比。进去小的点,然后再加一次就可以了。而最后那一次就是先到先得。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const long long maxn=2e5+5;
int a[maxn];
int l[maxn],r[maxn],op[maxn],ans[maxn],m,n,y;
vector<int> q;
int f(int x)
{
	int lt=1,rt=m-1,ans=m;
	while(lt<=rt)
	{
		int mid=(lt+rt)>>1;
		if(l[mid]+r[mid]<=x) lt=mid+1;
		else rt=mid-1,ans=mid;
	}
	return ans;
}
signed main()
{
	freopen("paint.in","r",stdin);
	freopen("paint.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++)
	{
		int x;cin>>x;
		y+=x;
		l[i]=min(l[i-1],y);
		r[i]=max(r[i-1],y);
	}
	for(int i=1;i<=m;i++) l[i]=abs(l[i]);
	ans[1]+=l[m],ans[n]+=r[m];
	for(int i=1;i<n;i++)
	{
		int x=a[i+1]-a[i];
		if(r[m]+l[m]<=x) ans[i]+=r[m],ans[i+1]+=l[m];
		else
		{
			int p=f(x);
			if(r[p]==r[p-1]) ans[i]+=r[p],ans[i+1]+=x-r[p];
			else ans[i+1]+=l[p],ans[i]+=x-l[p];
		}
	}
	for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
	return 0;
}
posted @ 2025-08-15 16:00  Engle_Chen  阅读(50)  评论(0)    收藏  举报