比赛链接:https://ac.nowcoder.com/acm/contest/3002
其他比赛题解:
【题解】2020牛客寒假算法基础集训营2
【题解】2020牛客寒假算法基础集训营3
【题解】2020牛客寒假算法基础集训营4
【题解】2020牛客寒假算法基础集训营5
试题目录
A - honoka和格点三角形(计算几何)
- 思路: 因为三角形面积为 1 且至少有一条边平行于 x 轴或者 y 轴,那么这条边的长度为 1 或者 2,所以枚举边长为 1 的平行于 x 轴和 y 轴的情况,加上边长为 2 的平行于 x 轴和 y 轴的情况,再减掉重复的,也就是同时存在边长为 1 和 2 都平行于坐标轴的情况。要注意的就是减掉后答案可能为负数,所以要加上 mod 防止出现负数。
Code:
#include <iostream>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
int main(){
ll n,m; cin>>n>>m;
ll ans1=((n-2)*2%mod)*((m-1)*m%mod)%mod;
ll ans2=((n-1)*2%mod)*((m-2)*m%mod)%mod;
ll ans3=((m-2)*2%mod)*((n-1)*n%mod)%mod;
ll ans4=((m-1)*2%mod)*((n-2)*n%mod)%mod;
ll ans5=((n-2)*2%mod)*((m-1)*2%mod)%mod;
ll ans6=((n-1)*2%mod)*((m-2)*2%mod)%mod;
ll ans=((ans1+ans2)%mod+(ans3+ans4)%mod-(ans5+ans6)%mod+mod)%mod;
cout<<ans<<endl;
return 0;
}
B - kotori和bangdream(签到)
- 思路: 数学期望期望等于总数乘以概率乘以分值。
Code:
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
int n,x,a,b; cin>>n>>x>>a>>b;
double ans = n*(x*0.01)*a+n*((100-x)*0.01)*b;
cout<<fixed<<setprecision(2)<<ans<<endl;
return 0;
}
D - hanayo和米饭(签到)
- 思路: 当时的想法是排个序,然后如果存在断续的,断续的那个数就是答案,如果不存在,就判断开头或者结尾的数是否存在,不存在的那个数就是答案。但其实有更简单的方法,就是先通过公式 (1+n)*n/2 求出 1 到 n 的总和,再减掉现有的数的总和就是答案。
Code:
#include <iostream>
#include <algorithm>
using namespace std;
int a[100010];
int main(){
int n; cin>>n;
for(int i=1;i<=n-1;i++)
cin>>a[i];
sort(a+1,a+n);
int vis=0;
for(int i=1;i<n-1;i++){
if(a[i]+1!=a[i+1]){
vis=1;
cout<<a[i]+1<<endl;
}
}
if(!vis){
if(a[1]!=1) cout<<1<<endl;
else if(a[n]!=n) cout<<n<<endl;
}
return 0;
}
E - rin和快速迭代(数论)
- 思路: 就是一道简单的求因子数的题目。可以分为两种,一种就是该数是完全平方数,那么最中间的因子数只有一个(其实就是两个相同的),一种就是不为完全平方数,那么因子数就是对称的。要注意的就是,判断一个数是否为完全平方数不能用 sqrt(n) * sqrt(n)==n来判断,要不然就得强制转换为 int 类型,即用 (int)sqrt(n) * (int)sqrt(n)==n 来判断。
Code:
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
ll ans;
ll f(ll x){
ll sum=0,cnt=0;
for(int i=1;i*i<=x;++i){
if(x%i==0){
if(i*i==x) sum++;
else cnt++;
}
}
sum+=cnt*2;
return sum;
}
int main(){
ios::sync_with_stdio(0);
ll n; cin>>n;
while(n!=2){
ans++;
n=f(n);
}
cout<<ans<<endl;
return 0;
}
G - eli和字符串(字符串)
- 思路: 由于字符串只由 26 种小写字母组成,那么可以先统计这 26 种字母分别的数量,同时统计每一种字母每一个的位置,如果存在相同字母数量大于等于 k 的就标记,同时记录此时子串的最小的长度,不断地更新。如果没有一种字母的数量大于等于 k 的就直接输出 -1 即可。具体见代码。
Code:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[30],b[30][200010];
int main(){
int ans=300000;
int n,k; cin>>n>>k;
string str; cin>>str;
int vis=0;
for(int i=0;i<str.length();i++){
a[str[i]-'a'+1]++;
b[str[i]-'a'+1][a[str[i]-'a'+1]]=i;
if(a[str[i]-'a'+1]>=k){
vis=1;
ans=min(ans,b[str[i]-'a'+1][a[str[i]-'a'+1]]-b[str[i]-'a'+1][a[str[i]-'a'+1]-k+1]+1);
}
}
if(!vis){
cout<<-1<<endl;
return 0;
}
if(k==1) cout<<1<<endl;
else
cout<<ans<<endl;
return 0;
}
H - nozomi和字符串(二分+字符串)
- 思路: 因为要求长度最大值,所以可以二分查找。统计 0 和 1 的前缀和,然后对于每个查找的长度,如果相应长度的 0 和 1 的数量和的最小值小于等于 k ,说明符合,继续不断查找,直到不符合即为最长。当然,如果 k 大于等于 n/2,那么长度一定是 n。
Code:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int n,k;
const int N=2e5+100;
int sum[N][2];
bool check(int x){
for(int i=1;i<=n-x;i++){
if(min(sum[i+x-1][0]-sum[i-1][0],sum[i+x-1][1]-sum[i-1][1])<=k)
return true;
}
return false;
}
int main(){
cin>>n>>k;
string str; cin>>str;
if(k>=n/2){
cout<<n<<endl;
return 0;
}
sum[1][str[0]-'0']=1;
for(int i=1;i<n;i++){
sum[i+1][0]=sum[i][0];
sum[i+1][1]=sum[i][1];
sum[i+1][str[i]-'0']++;
}
int l=1,r=n,ans;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
ans=mid;
l=mid+1;
}
else
r=mid-1;
}
cout<<ans<<endl;
return 0;
}
I - nozomi和字符串(dp+字符串)
- 思路: 其实是一道简单 dp 的字符串题目。
Code:
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=3e5+10;
ll dp[N];
int main()
{
int n,a,b,c; cin>>n>>a>>b>>c;
string str; cin>>str;
ll ans=0;
for(int i=0;i<n;i++){
dp[i]=dp[i-1];
if(i-3>=0 && str.substr(i-3,4)=="nico")
dp[i]=max(dp[i],dp[i-4]+a);
if(i-5>=0 && str.substr(i-5,6)=="niconi")
dp[i]=max(dp[i],dp[i-6]+b);
if(i-9>=0 && str.substr(i-9,10)=="niconiconi")
dp[i]=max(dp[i],dp[i-10]+c);
}
cout<<dp[n-1]<<endl;
return 0;
}