A
分析
签到题
C++代码
#include<iostream>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n,a,b,sum=0;
cin>>n>>a>>b;
if(a*2>b){
sum+=n/2*b+n%2*a;
}else sum+=n*a;
cout<<sum<<endl;
}
return 0;
}
B
分析
用最小的数作为矩阵第一个数,map记录给出的每个数的个数,然后直接根据公式判断算出来的数是否存在即可
C++代码
#include<iostream>
#include<map>
using namespace std;
int n,c,d;
void solve(){
cin>>n>>c>>d;
int a[n+5][n+5];
int sta=1e9,flag=0;
map<int,int> cnt;
for(int i=1;i<=n*n;i++){
int x;
cin>>x;
cnt[x]++;
sta=min(sta,x);
}
a[1][1]=sta;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==1&&j==1)continue;
else if(i==1&&cnt[a[1][j-1]+d]){//第一行
a[1][j]=a[1][j-1]+d;
cnt[a[i][j]]--;
}else if(j==1&&cnt[a[i-1][1]+c]){//第一列
a[i][1]=a[i-1][1]+c;
cnt[a[i][j]]--;
}else if(i>1&&j>1&&cnt[a[i-1][j]+c]&&a[i-1][j]+c==a[i][j-1]+d){
a[i][j]=a[i-1][j]+c;
cnt[a[i][j]]--;
}else flag=1;
}
if(flag)puts("NO");
else puts("YES");
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
C
分析
一共攻击k次,假设所有的a[i]的总和为s
1、k>=s,所有船都会被打爆,可以沉n艘船
2、k<s,左边会打 (k+1)/2 次,右边打 k/2 次,设置前缀和和后缀和(也可以不设置,做的时候实时更新即可),然后判断从前往后第几艘船打不下来,从后往前第几艘船打不下来,然后计算答案
C++代码
#include<iostream>
using namespace std;
typedef long long LL;
const int N=200010;
LL a[N],s[N],last[N];//s[i]前缀和,last[i]后缀和
void solve(){
LL n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i],s[i]=s[i-1]+a[i];
last[n+1]=0;
for(int i=n;i>=1;i--)last[i]=last[i+1]+a[i];
if(k>=s[n]){//k大于等于所有的a[i]的和,则一定可以让所有的船都沉
cout<<n<<endl;
return;
}
//一共k步,左边k/2上取整步,右边k/2下取整步
LL left=(k+1)/2,right=k/2,ans=0;
for(int i=1;i<=n;i++)
if(s[i]>left){
ans+=i-1;//第i艘船打不下来,i前面的所有船都可以沉
break;
}
for(int i=n;i>=1;i--)
if(last[i]>right){
ans+=n-i;//第i艘船打不下来,i后面的所有船都可以沉
break;
}
cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
D
分析
这题的做法类似于滑动窗口,但是没有真的把滑动窗口写出来,而是每次直接记录滑动窗口内的每个数的个数,判断其中可以与b数组里面的数匹配的数的个数,简称有效数的个数
由于每个数都可能有重复元素,所以在累计有效数的时候要判断一下是否该数的个数已经超过b数组中该数的个数,超过则超过部分无效,删除的时候类似
C++代码
#include<iostream>
#include<map>
using namespace std;
const int N=200010;
int a[N],b[N];
void solve(){
int n,m,k,ans=0;
cin>>n>>m>>k;
//mp记录b[i]中的数及出现次数,mp1记录滑动窗口中的数及出现次数
map<int,int> mp,mp1;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)cin>>b[i],mp[b[i]]++;
int cnt=0;//记录有效的数的个数
for(int i=1;i<=n;i++){
if(i<=m){
mp1[a[i]]++;
if(mp1[a[i]]<=mp[a[i]])cnt++;
if(i==m&&cnt>=k)ans++;
}else{
//当前滑动窗口内的数是a[i-m+1~i],所以要把a[i-m]踢出窗口
mp1[a[i-m]]--;
//如果删之前mp1[a[i-m]]<=mp[a[i-m]],则删除后,窗口内有效的数就减少一个
if(mp1[a[i-m]]<mp[a[i-m]])cnt--;
//把a[i]加入滑动窗口
mp1[a[i]]++;
if(mp1[a[i]]<=mp[a[i]])cnt++;//mp1[a[i]]<=mp[a[i]],有效的数增加一个
if(cnt>=k)ans++;
}
}
cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
E
分析
直接从大到小枚举k,只要遇到可行的k就跳出循环直接输出
对于每个k,从前往后遍历原数组a,每次遇到0就修改从当前位置往后的k个数,直接修改是不可取的,会超时,修改区间可以借助差分来解决
C++代码
#include<iostream>
using namespace std;
const int N=5010;
char s[N];
int n,k;
void solve(){
scanf("%d%s",&n,s+1);
for(k=n;k>=1;k--){
int a[N];
for(int i=1;i<=n;i++)a[i]=s[i]-'0';
//把a变成a的差分数组,一定要倒着异或,否则就会用已经异或的值异或后面的数
for(int i=n;i>=1;i--)a[i]^=a[i-1];
int flag=0;
for(int i=1;i<=n;i++){
a[i]^=a[i-1];//求出a[i]
if(a[i]==0){//如果a[i]=0,则直接翻转修改区间[i,i+k-1]
if(i+k-1>n){//i+k-1>n,则不能修改,此时的k不可行,flag=1
flag=1;
break;
}
//修改a[i ~ i+k-1]的区间
a[i]^=1,a[i+k]^=1;
}
}
if(!flag)break;
}
cout<<k<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
F
分析
根据题目,可以挖掘出Bob赢的一些充分条件:
1、4的个数必须为偶数
2、1,2,3的个数的奇偶性相同
每次只能删一个数,所以4对答案的贡献为d/2(假设当前1,2,3的奇偶性已经相同,则可以一直删4,直到删完为止。删的时候会发现(d+1)/2个奇数d/2个偶数,所以对答案的贡献为d/2)
同理,对于1,2,3也一样,假设当前2,3的奇偶性相同,则一直删1,每删一个,就变一次奇偶性,贡献也是a/2,2和3同理,贡献为b/2和c/2
但是1,2,3的个数最开始都是奇数时,每个数贡献a/2,b/2,c/2后,最后剩下的情况就是1,1,1,0,此时的贡献也为1
C++代码
#include<iostream>
using namespace std;
//4的个数为偶数, 1、2、3的个数的奇偶性相同
void solve(){
int a,b,c,d,ans=0;
cin>>a>>b>>c>>d;
if(a%2&&b%2&&c%2)ans++;
ans+=a/2+b/2+c/2+d/2;
cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
G
分析
由于路径是从(1,1)开始的,所以最大公约数一定是他的约数,直接从前往后枚举他的每一个约数即可,由于约数都是成对出现的,所以直接用根号n的时间复杂度枚举约数
对于每个约数k,用dp判断是否可以成立
dp[i][j]:
集合:所有从(1,1)走到(i,j)的所有选法
属性:bool(是否存在一条路中每个数都是k的倍数)
C++代码
#include<iostream>
using namespace std;
const int N=110;
int a[N][N],dp[N][N];
int n,m;
bool check(int k){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(i==1&&j==1)dp[i][j]=1;
else if(a[i][j]%k==0&&(dp[i-1][j]||dp[i][j-1]))dp[i][j]=1;
else dp[i][j]=0;
}
return dp[n][m]==1;
}
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j],dp[i][j]=0;
int maxx=0;
for(int i=1;i*i<=a[1][1];i++){//枚举a[1][1]的所有因子
if(a[1][1]%i==0){
if(check(i))maxx=max(maxx,i);
if(check(a[1][1]/i)){
maxx=max(maxx,a[1][1]/i);
break;
}
}
}
cout<<maxx<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}