幸运地水了个国一,应该是对了3个结果填空,2+0.5+0.5个编程大题。
此次国赛共有十个题:
A~E 五个结果填空,F~J 五个编程大题。
目前并未找到标准答案,个人解法可能并不完全正确或最优,不对的还请指正!
A-平方序列:
小明想找到两个正整数X和Y,满足
1、2019<X<Y
2、20192、X2、Y2构成等差数列
请你求出在所有可能的解中,X+Y的最小值是多少?
核心思想:
一重循环。
从2020开始遍历X的值,对于每一个X的值,利用等差数列的条件求出Y2的值,只需要判断开方得到的Y是不是整数即可。(可以将Y再次平方,看数值是否发生变化)
代码如下:
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int t=2019*2019;
for(int x=2020;;x++)
{
int x2=x*x;
int y2=2*x2-t;
int y=sqrt(y2);
if(y*y==y2)
{
cout<<x+y<<endl;
break;
}
}
return 0;
}
答案:
7020
B-质数拆分:
将2019拆分为若干个两两不同的质数之和,一共有多少种不同的方法?注意交换顺序视为同一种方法,例如2 + 2017 = 2019与2017 + 2 = 2019视为同一种方法。
核心思想:
先用欧拉线性筛法将素数筛出来,存在pre数组中。
dp[i][pre[j]]表示满足下列额外条件的分解方案数:
1、被分解数为i
2、分解出的素数最大值恰好为pre[j]
状态转移方程:
for(int k=0;k<j&&pre[k]<=i-pre[j];k++)
dp[i][pre[j]]+=dp[i-pre[j]][pre[k]];
代码如下:
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e4+20;
const int M=2019;
int pre[N],cnt;
bool vis[N];
ll dp[N][N];
void xss()
{
for(int i=2;i<N-10;i++)
{
if(!vis[i])
pre[cnt++]=i;
for(int j=0;j<cnt&&i*pre[j]<N-5;j++)
{
vis[i*pre[j]]=1;
if(i%pre[j]==0)break;
}
}
return;
}
int main()
{
xss();
for(int i=0;pre[i]<=M;i++)
dp[pre[i]][pre[i]]=1;
for(int i=2;i<=M;i++)
{
for(int j=0;pre[j]<=i;j++)
{
for(int k=0;k<j&&pre[k]<=i-pre[j];k++)
dp[i][pre[j]]+=dp[i-pre[j]][pre[k]];
}
}
ll ans=0;
for(int i=0;pre[i]<=M;i++)
ans+=dp[M][pre[i]];
cout<<ans<<endl;
return 0;
}
答案:
55965365465060
C-拼接:
小明要把一根木头切成两段,然后拼接成一个直角。如下图所示,他把中间部分分成了nXn的小正方形,他标记了每个小正方形属于左边还是右边。然后沿两边的分界线将木头切断,将右边旋转向上后拼接在一起。
要求每个小正方形都正好属于左边或右边,而且同一边的必须是连通的。在拼接时,拼接的部位必须保持在原来大正方形里面。请问,对于7的小正方形,有多少种合法的划分小正方形的方式。
题解待更新……
D-求值:
学习了约数后,小明对于约数很好奇,他发现,给定一个正整数t,总是可以找到含有t个约数的整数。小明对于含有t个约数的最小数非常感兴趣,并把它定义为St。例如S1= 1,S2= 2,S3= 4,S4= 6,。现在小明想知道,当t= 100时,St是多少?即S100是多少?
核心思想:
两重for循环暴力。
代码如下:
#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
for(int i=1;;i++)
{
int sum=0;
for(int j=1;j<=i;j++)
{
if(i%j==0)
sum++;
}
if(sum==100)
{
cout<<i<<endl;
break;
}
}
return 0;
}
答案:
45360
E-路径计数:
有一个7X7的方格。方格左上角顶点坐标为(0,0),右下角坐标为(7,7)。
求满足下列条件的路径条数:
1、起点和终点都是(0,0)
2、路径不自交
3、路径长度不大于12
4、对于每一个顶点,有上下左右四个方向可以走,但是不能越界。
核心思想:
dfs,根据路径长度不大于12来剪枝。
代码如下:
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=25;
int ans;
bool vis[N][N];
int b[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
void dfs(int x,int y,int k)
{
if(k>12)
return;
for(int i=0;i<4;i++)
{
int tx=x+b[i][0];
int ty=y+b[i][1];
if(!tx&&!ty&&k+1>2&&k+1<=12)
ans++;
if(tx<0||tx>7||ty<0||ty>7)continue;
if(vis[tx][ty])continue;
vis[tx][ty]=1;
dfs(tx,ty,k+1);
vis[tx][ty]=0;
}
return;
}
int main()
{
vis[0][0]=1;
dfs(0,0,0);
vis[0][0]=0;
cout<<ans<<endl;
return 0;
}
答案:
206
F-最优包含:
题目给定两个字符串S和T,保证S的长度不小于T的长度,问至少修改S的多少个字符,可以令T成为S的子序列。
输入描述:
两行。
第一行是字符串S,第二行是字符串T。
保证S的长度不小于T的长度,S的长度范围在10~1000之间。
输出描述:
答案,一个非负整数。
输入样例:
XBBBBBAC
ACC
输出样例:
2
核心思想:
动态规划。
dp[i][j]表示令T的前j个字符成为S的前i个字符的子序列需要修改的字符个数。
先初始化i=j和j=0的情况。
状态转移方程:
if(s[i]==t[j])
dp[i][j]=dp[i-1][j-1];
else
dp[i][j]=min(dp[i-1][j],dp[i-1][j-1]+1);
代码如下:
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
const int N=1e3+10;
int dp[N][N];
string s,t;
int main()
{
cin>>s>>t;
int ls=s.size();
int lt=t.size();
if(s[0]!=t[0])
dp[0][0]=1;
for(int i=1;i<lt;i++)
if(s[i]==t[i])
dp[i][i]=dp[i-1][i-1];
else
dp[i][i]=dp[i-1][i-1]+1;
for(int i=1;i<ls;i++)
{
if(s[i]==t[0])
dp[i][0]=0;
else
dp[i][0]=dp[i-1][0];
}
for(int j=1;j<lt;j++)
for(int i=j+1;i<ls;i++)
if(s[i]==t[j])
dp[i][j]=dp[i-1][j-1];
else
dp[i][j]=min(dp[i-1][j],dp[i-1][j-1]+1);
printf("%d\n",dp[ls-1][lt-1]);
return 0;
}
G-排列数:
对于一个数列中的某个数,如果这个数比两侧的数都大或比两侧的数都小,我们称这个数为这个数列的一个转折点。
如果一个数列有t个转折点,我们称这个数列为t+1调数列。
给定两个正整数n,k。求在1~n的全排列中,有多少个数列是k调数列。
输入描述:
两个正整数n,k。
输出描述:
答案,一个整数。
输入样例:
4 2
输出样例:
12
核心思想:
利用全排列的递归函数,剪枝,水数据。
代码如下:
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e3+10;
int n,k,ans,a[N];
void swap(int x,int y)
{
int t=a[x];
a[x]=a[y];
a[y]=t;
return;
}
bool pd(int x)//判断a[x]是不是转折点
{
if(a[x]>a[x-1]&&a[x]>a[x+1]||a[x]<a[x-1]&&a[x]<a[x+1])
return 1;
return 0;
}
void dfs(int m,int sum)//m为递归层数,sum为已经有的转折点数量
{
if(m>2&&pd(m-2))//判断a[m-2]是不是转折点
sum++;
if(sum>k-1||sum+n-m<k-1)//转折点过多或者过少都剪枝
return;
if(m==n-1)//全排列递归出口
{
if(m>1&&pd(m-1))
sum++;
if(sum==k-1)
ans++;
return;
}
for(int i=m;i<n;i++)//全排列函数的函数主体
{
swap(m,i);
dfs(m+1,sum);
swap(m,i);
}
return;
}
int main()
{
cin>>n>>k;
for(int i=0;i<n;i++)//初始化数组
a[i]=i+1;
dfs(0,0);
cout<<ans<<endl;
return 0;
}
H-解谜游戏:
小明正在玩一款解谜游戏。谜题由24根塑料棒组成,其中黄色塑料棒4根,红色8根,绿色12根(后面用Y表示黄色、R表示红色、G表示绿色)。初始时这些塑料棒排成三圈,如上图所示,外圈12根,中圈8根,内圈4根。小明可以进行三种操作:
1.将三圈塑料棒都顺时针旋转一个单位。例如当前外圈从0点位置开始顺时针依次是YRYGRYGRGGGG,中圈是RGRGGRRY,内圈是GGGR。那么顺时针旋转一次之后,外圈、中圈、内圈依次变为:GYRYGRYGRGGG、YRGRGGRR和RGGG。
2.将三圈塑料棒都逆时针旋转一个单位。例如当前外圈从0点位置开始顺时针依次是YRYGRYGRGGGG,中圈是RGRGGRRY,内圈是GGGR。那么逆时针旋转一次之后,外圈、中圈、内圈依次变为:RYGRYGRGGGGY、GRGGRRYR和GGRG
3.将三圈0点位置的塑料棒做一个轮换。具体来说:外圈0点塑料棒移动到内圈0点,内圈0点移动到中圈0点,中圈0点移动到外圈0点。例如当前外圈从0点位置开始顺时针依次是YRYGRYGRGGGG,中圈是RGRGGRRY,内圈是GGGR。那么轮换一次之后,外圈、中圈、内圈依次变为:RRYGRYGRGGGG、GGRGGRRY和YGGR。小明的目标是把所有绿色移动到外圈、所有红色移动中圈、所有黄色移动到内圈。给定初始状态,请你判断小明是否可以达成目标。
输入描述:
第一行包含一个整数T,代表询问的组数。(1<T<100)。每组询问包含3行:第一行包含12个大写字母,代表外圈从0点位置开始顺时针每个塑料棒的颜色。第二行包含8个大写字母,代表中圈从0点位置开始顺时针每个塑料棒的颜色。第三行包含4个大写字母,代表内圈从0点位置开始顺时针每个塑料棒的颜色。
输出描述:
对于每组询问,输出一行YES或者NO,代表小明是否可以达成目标。
输入样例:
2
GYGGGGGGGGGG
RGRRRRRR
YRYY
YGGGRRRRGGGY
YGGGRRRR
YGGG
输出样例:
YES
NO
题解待更新……
I-第八大奇迹:
在一条R河流域,繁衍着一个古老的名族Z。他们世代沿河而居,也在河边发展出了璀璨的文明。
Z族在R河沿岸修建了很多建筑,最近,他们热衷攀比起来。他们总是在比谁的建筑建得最奇特。
幸好Z族人对奇特的理解都差不多,他们很快给每栋建筑都打了分,这样评选谁最奇特就轻而易举了。
于是,根据分值,大家很快评出了最奇特的建筑,称为大奇迹。
后来他们又陆续评选了第二奇特、第二奇特、…、第七奇特的建筑,依次称为第二大奇迹、第三大奇迹、…、第七大奇迹。
最近,他们开始评选第八奇特的建筑,准备命名为第八大奇迹。在评选中,他们遇到了一些问题。
首先,Z族一直在发展,有的建筑被拆除又建了新的建筑,新建筑的奇特值和原建筑不一样,这使得评选不那么容易了。
其次,Z族的每个人所生活的范围可能不一样,他们见过的建筑并不是所有的建筑,他们坚持他们自己所看到的第八奇特的建筑就是第八大奇迹。
Z族首领最近很头疼这个问题,他害怕因为意见不一致导致Z族发生分歧。他找到你,他想先了解一下,民众自己认为的奇迹是怎样的。
现在告诉在R河周边的建筑的变化情况,以及在变化过程中一些人的生活范围,请编程求出每个人认为的第八大奇迹的奇特值是多少
输入描述:
输入的第一行包含两个整数L,N,分别表示河流的长度和要你处理的信息的数量。开始时河流沿岸没有建筑,或者说所有的奇特值为0。接下来N行,每行一条你要处理的信息。
如果信息为C p x,表示流域中第p个位置(1<=p<=L)建立了一个建筑,其奇特值为x。如果这个位置原来有建筑,原来的建筑会被拆除。
如果信息为Q a b,表示有个人生活的范围是河流的第a到b个位置(包含a和b,a<=b),这时你要算出这个区间的第八大奇迹的奇特值,并输出。如果找不到第八大奇迹,输出0。
输出描述:
对于每个为Q的信息,你需要输出一个整数,表示区间中第八大奇迹的奇特值。
输入样例:
10 14
C 1 5
C 2 4
C 3 7
C 4 6
C 5 5
C 6 1
C 7 8
Q 1 10
C 8 3
C 9 6
C 10 3
Q 1 9
C 6 10
Q 1 10
输出样例:
0
3
4
核心思想:
线段树+sort
线段树基础戳这里!
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e5+20;
int ans[17];//用于输出
char s[12];//用于读入字符C和Q
struct node{
int l,r;
int a[17];
}tr[N<<2];
bool pd(int x,int y)//sort排序依据
{
if(x>y)
return 1;
return 0;
}
void pushup(int m)//线段树回溯函数
{
for(int i=1;i<=8;i++)
tr[m].a[i]=tr[m<<1].a[i];
for(int i=9;i<=16;i++)
tr[m].a[i]=tr[m<<1|1].a[i-8];
sort(tr[m].a+1,tr[m].a+17,pd);//及时sort
return;
}
void build(int m,int l,int r)//建立线段树
{
tr[m].l=l;
tr[m].r=r;
if(l==r)
{
memset(tr[m].a,0,sizeof(tr[m].a));
return;
}
int mid=(l+r)>>1;
build(m<<1,l,mid);
build(m<<1|1,mid+1,r);
pushup(m);
return;
}
void update(int m,int x,int y)//更新数据
{
if(tr[m].l==x&&tr[m].r==x)
{
tr[m].a[1]=y;
return;
}
int mid=(tr[m].l+tr[m].r)>>1;
if(x<=mid)
update(m<<1,x,y);
else
update(m<<1|1,x,y);
pushup(m);
return;
}
void query(int m,int l,int r)//查询数据
{
if(tr[m].l==l&&tr[m].r==r)
{
for(int i=9;i<=16;i++)
ans[i]=tr[m].a[i-8];
sort(ans+1,ans+17,pd);//及时sort
return;
}
int mid=(tr[m].l+tr[m].r)>>1;
if(r<=mid)
query(m<<1,l,r);
else if(l>mid)
query(m<<1|1,l,r);
else
{
query(m<<1,l,mid);
query(m<<1|1,mid+1,r);
}
return;
}
int main()
{
int n,k,x,y;
cin>>n>>k;
build(1,1,n);
for(int i=0;i<k;i++)
{
scanf("%s%d%d",s,&x,&y);
if(s[0]=='C')
{
update(1,x,y);
}
else
{
for(int j=1;j<=8;j++)
ans[j]=0;
query(1,x,y);
printf("%d\n",ans[8]);
}
}
return 0;
}
J-燃烧权杖:
小C最近迷上了一款游戏。现在,在游戏中,小C有一个英雄,生命值为x;敌人也有一个英雄,生命值为y。除此以外,还有k个士兵,生命值分别为a1、a2、…、ak。
现在小C打算使用一个叫做“燃烧权杖”的技能。“燃烧权杖”会每次等概率随机选择一个活着的角色(英雄或士兵),扣减其10点生命值,然后如果该角色的生命值小于或等于0,则该角色死亡,不会再被“燃烧权杖”选中。“燃烧权杖”会重复做上述操作,直至任意一名英雄死亡。
小C想知道使用“燃烧权杖”后敌方英雄死亡(即,小C的英雄存活)的概率。为了避免精度误差,你只需要输出答案模一个质数p的结果,具体见输出格式。
输入描述:
输入包含多组数据。输入第一行包含一个正整数T,表示数据组数。接下来T组,每组数据第一行包含四个非负整数x、y、p、k,分别表示小C的英雄的生命值、敌方英雄的生命值,模数和士兵个数。第二行包含k个正整数a1、a2、…、ak,分别表示每个士兵的生命值
输出描述:
对于每组数据,输出一行一个非负整数,表示答案模质数p的余数。可以证明,答案一定为有理数。设答案为a/b(a和b为互质的正整数),你输出的数为x,则你需要保证a与bx模p同余;也即,x= (a*b-1)mod p,其中b-1表示b模p的逆元,mod为取模运算。
题解待更新……