信息学竞赛出题纪实

205 阅读12分钟

【YSYZ OI 】Round 2 出题纪实搬题纪实

第一次给小学初中生的同学们出题还是正式一点(指的是自己出数据,没有直接用洛谷的高级团队服务)。从2023年12月4日23:30肝到2023年12月5日2:30.整整3个小时。 写了如下目录树的文件 image 先是写了一份喜闻乐见的比赛说明.md,耗时半个小时。 然后开始搬运T1,改了一下题目描述,写了T1的题目描述.md,耗时十分钟。具体内容如下

game

题目描述

在玩一个小游戏。假设当前玩家得到一个整数。轮到玩家时,该玩家可以对这个数加或者减先开始。如果操作后,可以被整除,那么他就赢了,输出。如果已经玩了轮了,而还没有赢,那么就赢了,此时输出

输入格式

输入第一行是一个整数,代表有组测试数据。 对于每组测试数据,只包含一个整数

输出格式

输出共行,对于每一行:如果赢,就输出,如果赢,输出

样例 #1

样例输入 #1

1
1

样例输出 #1

First

样例 #2

样例输入 #2

6
1
3
5
100
999
1000

样例输出 #2

First
Second
First
First
Second
First

提示

对于 的数据,保证
对于 的数据,保证
对于 的数据,保证
对于 的数据,保证 。 然后对着题目描述造数据,写了一个造数据的程序in.cpp,耗时5分钟,如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
 char in[50]="2.in";
 string s,t;
    srand(time(0));
 for(int i=1;i<=10;i++)
 {
  sprintf(in,"data//%d.in",i);
  freopen(in,"w",stdout);
  if(i<=1)
        {
            int t=1,x=rand()%3+1;
            cout<<t<<endl;
            cout<<x<<endl;
        }
        else if(i<=3)
        {
            int t=10,x;
            cout<<t<<endl;
            for(int j=1;j<=t;j++)
            {
                cout<<rand()%100+1<<endl;
            }
        }
        else if(i<=9)
        {
            int t=10,x;
            cout<<t<<endl;
            for(int j=1;j<=t;j++)
            {
                long long x=rand();
                cout<<x*x*x%(int(1e9))+1<<endl;
            }
        }
        else 
        {
            int t=10,x;
            cout<<t<<endl;
            for(int j=1;j<=t;j++)
            {
                long long x=rand(),mo=1e18;
                cout<<x*x*x*x%mo+1<<endl;
            }
        }
 }
 
 
 return 0;
}

然后新建std.cpp,copy直接的AC代码,耗时1分钟。代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
 int t;
    long long n;
 cin>>t;
 while(t--)
 {
  cin>>n;
  if((n+1)%3==0||(n-1)%3==0cout<<"First"<<endl;
  else cout<<"Second"<<endl;
 }
 return 0;
}

创建data文件夹,放数据用。然后分别运行in.cpp,std.cpp。分别产生了10个.in文件。以及std.exe,一定要运行std.cpp产生std.exe,因为待会儿的out.cpp要调用它。 接下来新建out.cpp,如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
 char in[50]="2.in",out[50];
 string s,t;
 for(int i=1;i<=10;i++)
 {
  sprintf(in,"data//%d.in",i);
  sprintf(out,"data//%d.out",i);
  freopen(in,"r",stdin);
  freopen(out,"w",stdout);
  system("std.exe");
 }
 
 
 return 0;
}

OK,T1搞定 开始出T2

play

题目描述

Bramble_MarshallWriter_TV在玩一个小游戏。游戏规则如下:每场游戏有很多局,每局游戏比分先到的人赢下这一局,每场游戏先胜局的人获胜(可以参考乒乓球比赛理解)。在旁边看他们玩游戏,但是看得太入迷了,以至于忘记了的具体数值。只记得他们比了局,以及每局获胜的人,请你判断整场最后谁获胜了,如果Bramble_Marshall获胜了,请输出,如果Writer_TV获胜了,请输出。如果都有可能,请输出

输入格式

输入第一行是一个整数,代表有组测试数据。 对于每组测试数据,先输入一个整数,代表比了局。接下来一行有个包含或者的字符。表示对应的局是谁赢了。

输出格式

输出共行,对于每一行:如果Bramble_Marshall获胜了,请输出,如果Writer_TV获胜了,请输出。如果都有可能,请输出

样例 #1

样例输入 #1

1
5
ABBAA

样例输出 #1

A

样例 #2

样例输入 #2

2
3
BBB
7
BBAAABA

样例输出 #2

B
A

样例 #3

样例输入 #3

4
20
AAAAAAAABBBAABBBBBAB
1
A
13
AAAABABBABBAB
7
BBBAAAA

样例输出 #3

B
A
B
A

样例1解释

考虑取值的可能性: 如果,意思是到分就赢一小局,每场先赢局的人胜利,显然第一局第一个人赢,第二局第二、三局第二个人赢,第四、五局第一个人赢,由于第一个人先赢三局,故输出 如果,意思是先拿分就赢一一局,每场先赢局的人胜利,显然第一局第一个人赢,由于第一个人先赢一局,已经赢了,故输出 可以证明,并没有其它取值的可能性,综上所述,输出

数据范围

对于 的数据,保证
对于 的数据,保证 且字符串中的字符全部为
对于 的数据,保证 。 t2的in.cpp。out.cpp和T1的一样就不放了。 std.cpp:

#include<bits/stdc++.h>
using namespace std;
int main()
{
 int t;
 cin>>t;
 while(t--)
 {
  int n;
  string s;
  cin>>n>>s;
  cout<<s[s.size()-1]<<endl;
 }
 return 0;
}

记得每道题都要新建data文件夹放数据。

#include<bits/stdc++.h>
using namespace std;
int main()
{
 char in[50]="2.in";
 string s,t;
    srand(time(0));
 for(int i=1;i<=10;i++)
 {
  sprintf(in,"data//%d.in",i);
  freopen(in,"w",stdout);
  if(i<=1)
        {
            int t=1,x=rand()%3+1;
            cout<<t<<endl;
            cout<<2<<endl;
            cout<<"AA"<<endl;
        }
        else if(i<=3)
        {
            int t=10,n=20;
            cout<<t<<endl;
            for(int j=1;j<=t;j++)
            {
                cout<<n<<endl;
                char a='A';
                if(rand()%2) a='A';
                else a='B';
                for(int k=1;k<=n;k++) cout<<a;
                cout<<endl;
            }
        }
        else 
        {
            int t=10000,n=20;
            cout<<t<<endl;
            for(int j=1;j<=t;j++)
            {
                cout<<n<<endl;
                char a='A';
                for(int k=1;k<=n;k++)
                {
                    if(rand()%2) a='A';
                    else a='B';
                    cout<<a;
                }
                cout<<endl;
            }
        }
 }
 
 
 return 0;
}

T3:

play

题目描述

有一行盒子,有些盒子是实心的,有些盒子是空的,他希望把所有空的盒子都灌上水,为此可以进行下面的两种操作:

  • 1 在一个空的盒子上灌水
  • 2 把一个装有水的盒子的水倒到另一个空盒子中。

如果在某个时刻,第个盒是空的,且第个盒子都是装有水的盒子,则它会立刻被灌上水,而且第个盒子的水不会消失。请使用最少的操作1次数把所有空盒子灌上水。你不用管操作2的次数。注意实心盒子无需灌水也无法灌水。

输入格式

输入第一行是一个整数,代表有组测试数据。 对于每组测试数据,先输入一个整数,代表比了局。接下来一行有个包含'.'或者'#'的字符。'.'表示对应的盒子是空的,'#'的表示对应的盒子是实心的。

输出格式

输出共行,对于每一行:输出最小的操作1次数。

样例 #1

样例输入 #1

1
3
...

样例输出 #1

2

样例 #2

样例输入 #2

2
7
##....#
7
..#.#..

样例输出 #2

2
5

样例 #3

样例输入 #3

2
4
####
10
#...#..#.#

样例输出 #3

0
2

样例1解释

先把第1和第3个盒子分别用操作1灌水,然后由于第1和第3个盒子有水,第2个盒子也会有水,这样所有盒子都有谁,使用了两次操作1。可以证明这是最少操作1的次数。

样例2解释

第一组测试数据,先把第3和第5个盒子分别用操作1灌水,然后由于第3和第5个盒子有水,第4个盒子也会有水。再用操作2把第4个盒子的水倒到第6个盒子中,然后由于第3和第5个盒子有水,又会把第4个盒子灌满。此时所有空心盒子都灌了水,使用了2次操作1。 第二组测试数据,显然要一个个灌,故而输出5。

数据范围

对于 的数据,保证
对于 的数据,保证 且保证字符串中的字符全部为
对于 的数据,保证 。 T3的std:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m;
void solve()
{
    cin>>n;
    string s;
    cin>>s;
    int ans=0,tot=0,f=0;
    for(int i=0;i<s.size();i++)
    {
        if(s[i]=='#') 
        {
            if(tot>2) f=1;
            else ans+=tot;
            tot=0;
        }
        else tot++;
    }
    if(tot>2) f=1;
    else ans+=tot;
    if(f) cout<<2<<endl;
    else cout<<ans<<endl;
}
int main()
{
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

T3的in.cpp

#include<bits/stdc++.h>
using namespace std;
int main()
{
 char in[50]="2.in";
 string s,t;
    srand(time(0));
 for(int i=1;i<=10;i++)
 {
  sprintf(in,"data//%d.in",i);
  freopen(in,"w",stdout);
  if(i<=1)
        {
            int t=1,x=rand()%3+1;
            cout<<t<<endl;
            cout<<2<<endl;
            cout<<".#"<<endl;
        }
        else if(i<=3)
        {
            int t=10,n=rand()%100+1;
            cout<<t<<endl;
            for(int j=1;j<=t;j++)
            {
                n=rand()%100+1;
                cout<<n<<endl;
                for(int k=1;k<=n;k++) cout<<'.';
                cout<<endl;
            }
        }
        else 
        {
            int t=100,n=100;
            cout<<t<<endl;
            for(int j=1;j<=t;j++)
            {
                cout<<n<<endl;
                char a='A';
                int tot=0;
                for(int k=1;k<=n;k++)
                {
                    if(rand()%2)
                    {
                        a='.';
                        tot++;
                        if(tot==3) a='#',tot=0;
                    }
                    else a='#';
                    cout<<a;
                }
                cout<<endl;
            }
        }
 }
 
 
 return 0;
}

T4:

play

题目描述

是一个喜欢数学的女孩,有一天在黑板上写了一些数字来考她。所有数字要么是,要么是,要么是。 每次操作可以选择两个不同的数字,然后擦掉它们,并写出与两个擦掉的数字不同的数字。比如,一开始黑板上有数字,假设这次选择擦掉,那么就必须写上,这些数字就变成了。 ,在一些操作之后,黑板上有没有可能只剩下一种类型的数字。如果可能,请用指定的格式输出。思考了许久没能找到答案,请你帮她解决这个问题吧。

输入格式

输入第一行是一个整数,代表有组测试数据。 对于每组测试数据,固定输入个整数,代表黑板上写了

输出格式

输出共行,对于每一行:输出个数,每个数只能是表示不能剩下对应数字,表示可以剩下对应数字。比如说输出,表示最后只能剩下,比如说输出,表示最终可以剩下或者,但不可能剩下

样例 #1

样例输入 #1

1
1 1 1

样例输出 #1

1 1 1

样例 #2

样例输入 #2

1
2 3 2

样例输出 #2

0 1 0

样例 #3

样例输入 #3

1
82 47 59

样例输出 #3

1 0 0

样例1解释

一开始,黑板上有数字1,2,3。如果一开始擦掉2,3,那么最后剩下1,1。所以最后剩下的数字都是1。如果一开始擦掉1,3,那么最后剩下2,2。所以最后剩下的数字都是2。 如果一开始擦掉1,2,那么最后剩下3,3。所以最后剩下的数字都是3。故输出1 1 1。

样例2解释

一开始,黑板上有数字1,1,2,2,2,3,3。显然如果一开始擦掉1,3,那么剩下1,2,2,2,2,3。再擦掉1,3,那么最后剩下2,2,2,2,2,所以最后剩下的数字都是2。可以继续尝试,会发现无论怎么擦都不会只留下1或者3,故输出0 1 0

数据范围

对于 的数据,保证
对于 的数据,保证 。 T4的std:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m;
void solve()
{
    int a,b,c;
    cin>>a>>b>>c;
    cout<<!((b+c)&1)<<" "<<!((a+c)&1)<<" "<<!((a+b)&1)<<endl;
}
int main()
{
    // freopen("1900B.in","r",stdin);
    // freopen("1900B.out","w",stdout);
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

T4的in.cpp

#include<bits/stdc++.h>
using namespace std;
int main()
{
 char in[50]="2.in";
 string s,t;
    srand(time(0));
 for(int i=1;i<=10;i++)
 {
  sprintf(in,"data//%d.in",i);
  freopen(in,"w",stdout);
  if(i<=1)
        {
            int t=1,x=rand()%100+1;
            cout<<t<<endl;
            cout<<x<<" "<<x<<" "<<x<<endl;
        }
        else 
        {
            int t=1e5;
            cout<<t<<endl;
            for(int j=1;j<=t;j++)
            {
                int a=rand()%100+1;
                int b=rand()%100+1;
                int c=rand()%100+1;
                cout<<a<<" "<<b<<" "<<c<<endl;
            }
        }
 }
 
 
 return 0;
}

题解.md如下 模拟赛题解

T1

原题链接:www.luogu.com.cn/problem/CF1… 不难发现,如果第一个人+1或者减1之后不能被3整除的情况下,第二个人不是智障的话,总会把数字还原,以让第一个人还是不能赢。所以(x+1)%==0或者(x-1)%3==0的情况下,第一个人必赢,否则必输。记得开'long long',否则只能拿90分。10轮之内不能决出胜负这是个迷惑条件,小小的诈骗了一手。 一份代码实现

#include<bits/stdc++.h>
using namespace std;
int main()
{
 int t,n;
 cin>>t;
 while(t--)
 {
  cin>>n;
  if((n+1)%3==0||(n-1)%3==0cout<<"First"<<endl;
  else cout<<"Second"<<endl;
 }
 return 0;
}

T2

原题链接:www.luogu.com.cn/problem/CF1… 诈骗题,故意用样例解释解释得非常详细,以带偏各位的思路。事实上,只要看最后一分是谁拿的就好了,最后一分谁拿的当然是谁赢,因为某个人赢了比赛就结束了。所以只要输出记录的最后一个字符。代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
 int t;
 cin>>t;
 while(t--)
 {
  int n;
  string s;
  cin>>n>>s;
  cout<<s[s.size()-1]<<endl;
 }
 return 0;
}

T3

原题链接:www.luogu.com.cn/problem/CF1… 不难发现,如果存在连续的3个'.',那么答案就是2。 如果不存在连续的3个'.',那么答案就是'.'的总个数。 扫一遍字符串就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m;
void solve()
{
    cin>>n;
    string s;
    cin>>s;
    int ans=0,tot=0,f=0;
    for(int i=0;i<s.size();i++)
    {
        if(s[i]=='#') 
        {
            if(tot>2) f=1;
            else ans+=tot;
            tot=0;
        }
        else tot++;
    }
    if(tot>2) f=1;
    else ans+=tot;
    if(f) cout<<2<<endl;
    else cout<<ans<<endl;
}
int main()
{
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

T4

原题链接:www.luogu.com.cn/problem/CF1… 对于任意的两个数,每次要么出现次数同时减一,要么一加一减,无论如何任意两数的次数之差(或之和)的奇偶性不变,因此猜结论:两数之差为偶数,可以只剩下第三个数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m;
void solve()
{
    int a,b,c;
    cin>>a>>b>>c;
    cout<<!((b+c)&1)<<" "<<!((a+c)&1)<<" "<<!((a+b)&1)<<endl;
}
int main()
{
    // freopen("1900B.in","r",stdin);
    // freopen("1900B.out","w",stdout);
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

本文使用 markdown.com.cn 排版