牛客小白月赛76_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ
A.猜拳游戏
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
void solve()
{
string s;
cin>>s;
cout<<s<<endl;
}
signed main()
{
solve();
return 0;
}
B.Kevin喜欢一
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
void solve()
{
int n;
cin>>n;
int sum=1;
int copy=1;
if(sum>=n){
cout<<0<<endl;
return;
}
for(int i=1;;i++){
sum+=copy;
copy=sum;
if(sum>=n){
cout<<i<<endl;
return;
}
}
}
signed main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
C.A加B,A模B
首先呢,如果对于k,从0一个一个找,有t(2e5)个样例,一定会超时,于是果断放弃这种方法,寻求其它方法,此时千万不要死磕,一直执着于此,一直苦恼
有2e5个样例,会想到每个样例可能是O(1),大概率是个思维题,应该有某些规律,应该重新读题面,从中寻找隐藏信息
可以从a mod b = m中提取一些信息,可得m小于b
a=n-b==>a=k*b+m(k>=0)
n-b=k*b+m==>n-m=(k+1)*b>0
b>m
n-m是个定值,要想b>m,能够成立,那么b要尽可能大,那么(k+1)尽可能小,所以k取0,此时b做到最大如果都不满足b>m,那么就不可能满足了,就输出-1
b就取n-m,a取m
b的范围是[1,1e9],以及b>=m如果不在这个范围内,那么b不合法,则输出-1,否则输出m和n-m
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<set>
using namespace std;
void solve()
{
int n,m;
cin>>n>>m;
if(n-m<1||n-m>1e9||n-m<=m) cout<<-1<<endl;
else cout<<m<<" "<<n-m<<endl;
}
signed main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
D.MoonLight的运算问题
首先,要知道这不会超时的,虽然t范围为1e5,n范围为2e5,但是数据保证了n的总和不超过2e5
我一开始的代码是这样写的:
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N=2e5+10,mod=998244353;
int f[N];
void solve()
{
int n;
cin>>n;
int res=0;
for(int i=0;i<n;i++){
int x;
cin>>x;
res=max(res*x,res+x);
res=res%mod;
}
cout<<res%mod<<endl;
}
signed main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
然后答案错误,用例通过率仅为44.44%,现在来分析一下这样写为什么是错的
首先,题目要求求出x的最大值,然后对998244353取模
但要注意x后面越来越大,可能会爆long long,所以需要一边操作,一边取模
如图,模运算
但是呢,这就是错因所在,因为题目要我们先求出x的最大值,然后再取模,而我们为了防止爆long long,必须一边操作一边取模,每次res都会取模,如果这样写的话,res=max(res*x,res+x),在比较谁大之前,前面一次循环res是取模了的,可能原本真实的res值是res*x大于res+x的,但是取模后的res值可能就是res*x小于res+x了,这样就出错了,所以我们应该依据真实的res值来选择执行哪个操作才能更大
首先,如果x小于等于1的话,不管res的真实值是多少,都应该执行res+=x,注意x大于等于0,所以res的真实值肯定是在不断变大的(当然,我们要选择最优的策略,可不能自己作死选择res*=0),那么当res一旦大于1之后,res的真实值就不会再减小了(而取模之后有可能减小的),此时如果x>1都执行res*=x操作
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<set>
#define int long long
#define endl '\n'
using namespace std;
const int mod=998244353;
int n;
void solve()
{
cin>>n;
int res=0;
bool flag=true;
for(int i=0;i<n;i++){
int x;
cin>>x;
if(x<=1||res<=1) res+=x;
else res*=x;
}
cout<<res<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--)
solve();
return 0;
}
E.括号操作序列专家
首先分别统计左括号和右括号的个数,如果个数不相等,肯定不能使括号序列变合法,直接输出-1
否则肯定有办法使得括号序列变合法
对于每一个左括号,一直往左移动,去匹配最左边的没有被匹配的右括号
具体我们这样操作:
以0为基准
如果遇到左括号.sum++
如果遇到右括号,sum--
sum如果小于0的话,sum的绝对值即表示此时左边还有多少个右括号没有被匹配
如果遇到左括号,并且sum小于0的话,那么说明该左括号的左边还有sum的绝对值个右括号还未被匹配,那么该左括号就需要往左移动(和左边相邻的交换)sum的绝对值次
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<set>
#define int long long
#define endl '\n'
using namespace std;
int n;
void solve()
{
cin>>n;
string s;
cin>>s;
int cnt=0;
for(int i=0;i<s.size();i++){
if(s[i]=='(') cnt++;//数一共有几个左括号
}
//特判,首先要保证左括号的个数和右括号的个数相同
if(cnt*2!=n){
cout<<-1<<endl;
return;
}
int res=0;
int sum=0;
for(int i=0;i<s.size();i++){
if(s[i]=='(') {
if(sum<0) res-=sum;
sum++;
}
else sum--;
}
cout<<res<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--)
solve();
return 0;
}