2024csp-s模拟赛1复盘

T1

限制
时间限制:3s。

空间限制:512MB。

问题描述

王牌校长波波牛手下有 n​ 个特工,每个特工有一个主密码和一个辅助密码。

当两个特工遇见,他们会向对方展示自己的辅助密码,然后计算自己的主密码与对方的辅助密码的和。如果两位特工的计算结果相同,则他们匹配成功。

有多少对不同的特工遇见时能匹配成功?

输入格式

第一行有一个整数 n​,代表特工个数 。

下面有 n​ 行,第 i+1​ 行有两个数字 xi​ 和 yi​,依次代表特工 i​ 的主密码和辅助密码。

输出格式

一个整数,表示遇见时能匹配成功的特工对数。

注意到答案可能较大,需要合适的存储和输出方式。

思路

首先要计算的就是xi-yj=xj-yi的个数,简单移项就变为xi+yi=xj+yj的个数,所以用一个unordered_map就可以解决这个问题

代码

#include<bits/stdc++.h>
using namespace std;
unordered_map<string,long long> mp;
int n;
long long ans;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
    cin>>n;
    for (int i=1;i<=n;i++) {
        long long x,y;
        cin>>x>>y;
        long long xy=x-y;
        string xz;
        if (xy<0){
        	xz="-";
        	xy=abs(xy);
		}
		while(xy){
			int wz=xy%10;
			xz+=('0'+wz);
			xy/=10; 
		}
//		cout<<xz<<"\n";
        ans+=mp[xz];
        mp[xz]++;
    }
    cout<<ans;
    return 0;
}

T2

问题描述

小 D 和小 Y 在玩一个叫提克塔可头的游戏。游戏在一个 3×3 的棋盘上进行。初始时棋盘上所有位置都是空的(用 . 表示)。从小 D 开始,两人交替行动。小 D 每次会选择一个空格子下一颗白子(用 X 表示),小 Y 会选择一个空格子下一颗黑子(用 O 表示)。当有出现某一行、某一列、或某条对角线上的三个格子里全都是某一方的棋子时,该方获胜。

小 D 和小 Y 下棋下累了,想考考你。他们给你看了 t 个提克塔可头棋盘。对每个棋盘,请你判断,棋盘上的局面是否是可以达到的。如果是,还请你计算出,从当前局面开始,有多少个棋局是小 D 赢,有多少个棋局是小 Y 赢。一个“棋局”是指从当前局面开始,直到下完这盘棋,过程中两人的所有行动。两个棋局不同,当且仅当存在某一方在某一步下的位置不同。

输入格式

第一行一个正整数 t。

接下来 t 行,每行一个长度为 9 的字符串,包含 ., X, O 三种字符,描述一个棋盘上的局面。前三个字符是棋盘的第一行,然后第二行、第三行。

输出格式

输出 t 行。每行两个整数。

如果该棋盘上的局面不可能达到,输出两个 −1。

否则,输出从当前局面开始,有多少个棋局是小 D 赢,有多少个棋局是小 Y 赢。

思路

这题我也是无语了,就是直接暴力把所有情况处理出来成一个DAG然后再用拓扑排序就可以得到所有答案了,最后O(1)输出,当时确实没有想到这么无脑的做法,话说想到了也写不出来吧

#include<bits/stdc++.h>
using namespace std;
const int N=19683;
int pw3[10];
int win[N];
vector<int> g[N];
int id[N];
int ans1[N],ans2[N];
int f(int st,int i,int j){
	return (st/pw3[8-((i-1)*3+j-1)])%3;
}
int checkw(int st){
	if (f(st,1,1)!=0&&f(st,1,1)==f(st,1,2)&&f(st,1,1)==f(st,1,3)) return f(st,1,1);
	if (f(st,2,1)!=0&&f(st,2,1)==f(st,2,2)&&f(st,2,1)==f(st,2,3)) return f(st,2,1);
	if (f(st,3,1)!=0&&f(st,3,1)==f(st,3,2)&&f(st,3,1)==f(st,3,3)) return f(st,3,1);
	if (f(st,1,1)!=0&&f(st,1,1)==f(st,2,1)&&f(st,1,1)==f(st,3,1)) return f(st,1,1);
	if (f(st,1,2)!=0&&f(st,1,2)==f(st,2,2)&&f(st,1,2)==f(st,3,2)) return f(st,1,2);
	if (f(st,1,3)!=0&&f(st,1,3)==f(st,2,3)&&f(st,1,3)==f(st,3,3)) return f(st,1,3);
	if (f(st,1,1)!=0&&f(st,1,1)==f(st,2,2)&&f(st,1,1)==f(st,3,3)) return f(st,1,1);	
	if (f(st,1,3)!=0&&f(st,1,3)==f(st,2,2)&&f(st,1,3)==f(st,3,1)) return f(st,1,3);
	return 0;
} 
void print(int st) {
	if (st>=N) {
		cout<<"invalid st "<<st<<"\n";
		exit(0);
	}
	for (int i=1;i<=3;i++) {
		for (int j=1;j<=3;j++) {
			cout<<f(st,i,j)<<" ";
		}
		cout<<"\n";
	}
}
void dfs(int st,int player){
	if (win[st]!=-1) return;
	win[st]=0;
	int w=checkw(st);
	if (w!=0){
		win[st]=w;
		return;
	}
	for (int i=1;i<=3;i++){
		for (int j=1;j<=3;j++){
			if (f(st,i,j)==0){
				g[st+pw3[8-((i-1)*3+j-1)]*player].push_back(st);
				id[st]++;
				dfs(st+pw3[8-((i-1)*3+j-1)]*player,3-player);
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	pw3[0]=1;
	for (int i=1;i<=9;i++) pw3[i]=pw3[i-1]*3;
	memset(win,-1,sizeof win);
	dfs(0,1);
	queue<int> q;
	for (int i=0;i<N;i++){
		if (!id[i]){
			if (win[i]==1) ans1[i]=1;
			if (win[i]==2) ans2[i]=1;
			q.push(i);
		}
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for (auto v:g[u]){
			ans1[v]+=ans1[u];
			ans2[v]+=ans2[u];
			id[v]--;
			if (!id[v]) q.push(v);
		}
	}
	for (int i=0;i<N;i++){
		if (win[i]==-1){
			ans1[i]=ans2[i]=-1;
		}
	}
	int t;
	cin>>t;
	while(t--){
		string s;
		cin>>s;
		int st=0;
		for (int i=0;i<=8;i++){
			int x=0;
			if (s[i]=='X') x=1;
			else if (s[i]=='O') x=2;
			st=st*3+x; 
		}
		cout<<ans1[st]<<" "<<ans2[st]<<"\n";
	}
	return 0;
}

T3

问题描述

小 D 和小 Y 是一起长大的好朋友。在他们的童年里,有许多相似的回忆。现在,小 D 和小 Y 已不再年轻,他们回想起过去的时光,希望你能帮他们找出这些相似的回忆。

我们把一个人的回忆,看做一个字符串 S[1…|S|],其中 |S| 表示字符串的长度,S[i] (1≤i≤|S|) 表示字符串第 i 位上的字符。

对于两段回忆 S[1…|S|],T[1…|T|],我们认为它们是相似的,当且仅当满足如下两个条件:

|S|=|T|。
对于所有二元组 (i,j) (1≤i≤j≤|S|),若 S[i]=S[j],则 T[i]=T[j];若 S[i]≠S[j],则 T[i]≠T[j]。
现在,小 D 把他的所有回忆 a[1…|a|] 告诉了你,而小 Y 则给了你一个回忆片段 b[1…|b|]。保证 |a|≥|b|。请你求出,a 中有多少子串与 b 是相似的。

我们称一个串是另一个串的子串,当且仅当前者能通过从后者的开头、结尾各删除若干字符(可以为 0,即不删)得到。

另外,由于小 D 和小 Y 的回忆非常丰富,不足以用有限的英文字母表示出来,所以我们用数字来表示这些串。每个数字代表串的一位。数字间用空格隔开。

输入格式

第一行读入两个正整数 |a|,|b|,分别表示小 D 的回忆、小 Y 的回忆片段的大小。保证 |a|≥|b|。

第二行读入 |a| 个正整数,分别表示 a[1]…a[|a|]。

第三行读入 |b| 个正整数,分别表示 b[1]…b[|b|]。

输出格式

输出一行一个非负整数,表示 a 中有多少子串与 b 是相似的。

思路

用一个类似于双指针的东西就可以解决

代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+7;
int n,m;
int a[N],b[N];
int p[N];
int ans;
unordered_map<int,int> mp; 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
	cin>>n>>m;	
	for (int i=1;i<=n;i++){
		int x;
		cin>>x;
		a[i]=i-mp[x];//上次出现此数字和这次的位置差 
		mp[x]=i;
	}
	mp.clear();
	for (int i=1;i<=m;i++){
		int x;
		cin>>x;
		b[i]=i-mp[x];//同理 
		mp[x]=i;
	}
	int r=1;
	p[1]=1;
	for (int l=2;l<=m;l++){
		while(r!=1&&b[l]!=b[r]&&(b[l]<r||b[r]<r)) r=p[r-1];
		if (b[l]==b[r]||(b[l]>=r&&b[r]>=r)) r++;
		p[l]=r;
	//	cout<<p[l]<<" ";
	}
	r=1;
	for (int l=1;l<=n;l++){
		while(r!=1&&a[l]!=b[r]&&(a[l]<r||b[r]<r)) r=p[r-1];
		if (a[l]==b[r]||(a[l]>=r&&b[r]>=r)) r++;
		if (r==m+1) ans++,r=p[r-1];
	}
	cout<<ans;
	return 0;
}

T4

问题描述

小 D 和小 Y 在波波牛一中停课训练信息学竞赛。

但是波波牛一中实行的是素质教育,要求学生全面发展,所以他们不时需要回班学一天文化课。

具体来说,小 D 在每个连续的 a 天里必须至少回班 1 天,小 Y 在每个连续的 b 天里必须至少回班 1​ 天。

但是,小 D 和小 Y 太调皮。如果他们同时回班,班里就会被搞得一团糟。所以,老师又规定,小 D 和小 Y 不得在同一天回班。

为了多花时间训练信息学竞赛,小 D 和小 Y 希望他们两人回班的总天数尽量少。请你帮他们算算,在接下来的 n​ 天里,两人总共至少需要回班多少天?

输入格式

输入一行,包含三个正整数:a, b, n。

输出格式

输出一行一个整数,表示他们至少需要回班多少天。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+9;
int ex(int a,int b,int &x,int &y){
	if (b==0){
		x=1;
		y=0;
		return a;
	}
	int d=ex(b,a%b,x,y);
	int t=x;
	x=y;
	y=t-(a/b)*y;
	return d;
}
int mem[N][2];
long long A,B,n;
int dfs(int len,int a,int b){
	if (len==0) return 0;
	int t=((a==A)?0:1);
	if (mem[len][t]!=-1)  return mem[len][t];
	int x,y; 
	int d=ex(a,b,x,y);
	if (d!=1){
		return len/b+(len+1)/a; 
	}
	x=(x%b+b)%b;
	if (a*x-1>len){
		return mem[len][t]=len/b+(len+1)/a;
	}
	return mem[len][t]=(a*x-1)/b+x+min(dfs(len-(a*x-1),a,b),dfs(len-(a*x-1),b,a));
}
int main(){
	cin>>A>>B>>n;
	long long lcm=A*B/__gcd(A,B);
	if (lcm>n){
		cout<<n/A+n/B;
		return 0; 
	}
	for (int i=1;i<=n;i++) mem[i][0]=mem[i][1]=-1;
	cout<<lcm/A+lcm/B+min(dfs(n-lcm,A,B),dfs(n-lcm,B,A));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值