A题
题意
T个测试样例,每个样例两行,第一行输入两个整数a,b第二行输入两个整数c,d
问在a后面加b个0和c后面加d个0比较,输出>,=,<号
思路
开始我是懵的,以为直接暴力,按字符串输入,然后在a后面加b个0,c后面加d个0,先比较长度,长的相等比较字典序
好的,算了一下时间,b和d最大10的6次方,好像超时。
后面想到的思路就是分类讨论
1、如果a>c且b>=d那么输出>
2、如果a==c且b==d那么输出=
3、如果a<c且b<=d那么输出<
4、如果a>c且b<d,这种情况比较难处理
我先让d--,d--的话就是c*10,一直循环处理,直到a<=c或者b==d结束循环
如果是a<c,退出循环,那么输出<
如果是a==c推出循环,判断b是否等于d,等于就输出=否则输出<
如果是b==d推出循环,如果a!=c那么输出>
5、如果a<c且b>d,处理情况同4
代码
#include<iostream>
using namespace std;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
long long a,b,c,d;
scanf("%lld %lld",&a,&b);
scanf("%lld %lld",&c,&d);
while(a%10==0)
{
a/=10;
b++;
}
while(c%10==0)
{
c/=10;
d++;
}
if(a>c&&b>=d)
printf(">\n");
else if(a<c&&b<=d)
printf("<\n");
else if(a==c&&b==d)
printf("=\n");
else if(a==c&&b>d)
printf(">\n");
else if(a==c&&b<d)
printf("<\n");
else if(a>c&&b<d)
{
while(a>c&&b!=d)
{
c*=10;
d--;
}
if(d==b)
{
if(a>c)
printf(">\n");
else if(a==c)
printf("=\n");
else
printf("<\n");
}
else
printf("<\n");
}
else if(a<c&&b>d)
{
while(a<c&&b!=d)
{
a*=10;
b--;
}
if(d==b)
{
if(a>c)
printf(">\n");
else if(a==c)
printf("=\n");
else
printf("<\n");
}
else
printf(">\n");
}
}
system("pause");
return 0;
}
B题
题意
n个正整数,求n/2(向下取整)对数a,b,要求a%b的值不在这n个数里面且a>b,题目保证有解
思路
其实我们只需要sort一下,然后拿其他的数mod最小的数就可以了
毕竟比最小的还小的数都是没有出现过的,而且mod最小的数得到的结果都是<最小的数的
因为保证n个数是正整数,所以即使最小的数是1,mod1的结果也还可以是没出现过的0
代码
#include<iostream>
#include<algorithm>
using namespace std;
bool used[1000005];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
int a[200005];
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
sort(a,a+n);
for(int i=0;i<n/2;i++)
printf("%d %d\n",a[i+1],a[0]);
}
//system("pause");
return 0;
}
C题
题意
h格血的龙,在n个时间里砍他一刀,每次砍了以后接下来k秒每秒掉一格血,两次砍的掉血不可叠加(即1秒只能掉一格血)
给出这n个时间点,求最小的k可以杀死龙
思路
没什么好说的,看完数据就知道,一道经典的二分题
对k进行二分,暴力模拟判断k是否可行
代码
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
long long a[105],h;
bool fun(long long mid)
{
long long all=mid,t=a[0];
for(int i=1;i<n;i++)
{
if(a[i]>=a[i-1]+mid)
all=all+mid;
else
all=all+a[i]-a[i-1];
}
if(all>=h)
return true;
else
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %lld",&n,&h);
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
long long l=1,r=1e18;
while(l<r)
{
long long mid=l+r>>1;
if(fun(mid))
r=mid;
else
l=mid+1;
}
cout<<l<<endl;
}
system("pause");
return 0;
}
D题
题意
有一个非负数数组,问有多少个子序列满足以下条件|xi-MEX(x1,x2,...,xi)|<=1,其中MEX表示集合种最小的未出现的非负整数。即序列满足每个数和前缀中没出现的最小非负整数相差不超过1.
思路
我当时看了电音哥的视频和窝老师的视频还没有理解,后面参考网上题解磕了一会才懂,是我dp太烂
参考的博客:https://www.cnblogs.com/zwjzwj/p/15635036.html
首先从头开始考虑,对于满足这个条件的式子,开头必定是0,1之一(这个很容易想的到)
1、首先考虑最简单的情况,1开头,这时候MEX是0
不能是大于1的数,大于1的话-0>1
所以1后面就只能是1或者0如此循环
2、考虑0开头,后面可以跟0,1,2
跟0的话,重复这个步骤
跟1的话,把MEX视为从2(因为0、1在前面出现过)开始没出现过的最小数,那么这时候1后面分两种情况
情况1,1后面跟3,这种情况是不是和1很像了?
1后面跟3以后3后面就只能是1或者3了
情况2,后面跟2,这时候其实可以看作MEX'是从2开始没出过的最小数,然后就是参考2的整个考虑了
跟2的话,这时候MEX就是1
所以跟2后面只能是2/0如此循环
这样考虑以后,好像就可以不断找出一些规律来了
满足这样的序列要么是连续不断的非严格递增序列的
如果发生断裂,那么一定是在满足最大数=次大数+2,此时次大数+1是MEX
按这样的方式考虑
如果连续不断不就是求一个数组的非严格递增子序列有多少种?明显是dp
如果断裂的话,也是在连续的过程中发生断裂,且和次大数有关
那么我们定义一个数组dp[i][3]
dp[i][0]表示i作为最大值且没有断裂方案数,dp[i][1]i作为最大值发生断裂,dp[i][2]i作为次大值发生断裂方案数
最后dp数组和就是答案
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int mod=998244353;
long long dp[500005][3];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
int a[500005];
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
//0 - i作为最大值且没有断裂,1 - i作为最大值发生断裂,2 - i作为次大值发生断裂
for(int i=0;i<=n+2;i++)
for(int j=0;j<3;j++)
dp[i][j]=0;
for(int i=0;i<n;i++)
{
//保存之前不断裂到a[i]的方案数
long long temp=dp[a[i]][0];
if(a[i]>0)//这时候得到的dp[a[i]][0]是不断裂到本次a[i]的次数(本次a[i]一定出现,之前的a[i]不保证出现)
//到a[i]不曾断裂,那么可以在a[i]-1的时候不选任何一个a[i]
//以及在选了任意若干个a[i]的情况下再选a[i]
dp[a[i]][0]=(dp[a[i]-1][0]+dp[a[i]][0]+mod)%mod;
else//a[i]==0的情况,那么对于前面出现的0去情况再加一个0就是出现本次0的方案数
dp[0][0]=(dp[0][0]+1)%mod;//这样总的0的方案数是temp+dp[0][0](这的dp[0][0]是前面式子计算后的)
dp[a[i]][0]=(temp+dp[a[i]][0]+mod)%mod;
//保存a[i]作为最大值断裂的方案数
//这时候a[0]的方案数一直为0,a[i]不能作为最大值发生断裂
temp=dp[a[i]][1];
if(a[i]>1)//a[i]作为最大值发生断裂,那么只能没有a[i]-1
//也就是次大值是a[i]-2
//这时候可以是到a[i]-2没有发生断裂,或者a[i]-2作为次大值发生过断裂
dp[a[i]][1]=(dp[a[i]][1]+(dp[a[i]-2][0]+dp[a[i]-2][2]+mod)%mod+mod)%mod;
else if(a[i]==1)//此时缺少了0,但是mex并不会是0
dp[a[i]][1]=(dp[a[i]][1]+1)%mod;//所以这时候情况解释参考dp[0][0]的情况
dp[a[i]][1]=(dp[a[i]][1]+temp+mod)%mod;
//保存a[i]作为次大值发生断裂的方案数
//次大值是a[i]且发生断裂,那么最大值一定是a[i]+2
//断裂的是a[i]+1
//那么此时可以接上的方案一个就是之前a[i]作为次大值发生断裂然后再加一个a[i]
//一个是a[i]+2作为最大值发生断裂然后加上本次的a[i]
temp=dp[a[i]][2];
dp[a[i]][2]=(dp[a[i]][2]+dp[a[i]+2][1]+mod)%mod;
dp[a[i]][2]=(temp+dp[a[i]][2]+mod)%mod;
}
long long ans=0;
for(int i=0;i<=n;i++)
for(int j=0;j<=2;j++)
ans=(ans+dp[i][j]+mod)%mod;
printf("%lld\n",ans);
}
system("pause");
return 0;
}
E题
题意
n*m的棋盘,.代表这格为空,#代表不可通过,L代表实验室位置
现在有一个东西在外面,你可以给他输入向上下左右走
但他不会听你的,只会朝你指令以外的方向走,如果没有可走的方向就不动
问在那些节点可以保证你输入指令以后不管他朝其他那个方向走都可以到实验室,这样的点改为+
你输入的指令可以由你选择
最后输出地图
思路
很简单嘛,从实验室开始bfs对可到达的点进行判断如果该点点可以走的方向<=2,那么他就是可以走到实验室的
代码
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int dx[4]={0,1,0,-1},dy[4]={-1,0,1,0};
void solve()
{
int n,m;
scanf("%d %d",&n,&m);
const int nn=n,mm=m;
char mp[nn+1][mm+1];
int a,b;
for(int i=0;i<n;i++)
{
scanf("%s",&mp[i]);
for(int j=0;j<m;j++)
if(mp[i][j]=='L')
{
a=i;
b=j;
}
}
queue<pair<int,int> > q;
q.push({a,b});
while(!q.empty())
{
int x,y;
x=q.front().first;
y=q.front().second;
//cout<<x<<" "<<y<<endl;
q.pop();
for(int i=0;i<4;i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx<0||xx>=n||yy<0||yy>=m||mp[xx][yy]!='.')//先确定xx和yy在界限范围内
continue;
int cnt=0;
for(int j=0;j<4;j++)
{
int aa=xx+dx[j],bb=yy+dy[j];
if(aa>=0&&aa<n&&bb>=0&&bb<m&&mp[aa][bb]=='.')
cnt++;
}
if(cnt<=1&&mp[xx][yy]=='.')
{
mp[xx][yy]='+';
q.push({xx,yy});
}
}
}
for(int i=0;i<n;i++)
{
printf("%s\n",mp[i]);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
solve();
}
system("pause");
return 0;
}
本场cf体验
本场cf打的稀烂,只过了三题
一个原因是有一段时间没打了状态不行(实际就是菜)
E题我居然没去看,写A题写了40+分钟,人没了
还好分低,没有掉分
最后,这场cf被人放鸽子了,气