花开堪折直须|

园龄:粉丝:关注:

2025牛客寒假算法基础集训营5

状态最拉跨的一场,很难受,脑袋转不动

A

注意数据范围,简单构造

J

按照题意模拟

点击查看代码
#include<bits/stdc++.h>

using namespace std;

int t;
int n,m;
string s;
void solve(){
    cin>>n;
    cin>>s;s=" "+s;
    int v=0;
    long long ans=0;
    for(int i=1;i<=n;++i){
        if(s[i]=='0'){
            v+=10;
            ans+=v;
        }
        else if(s[i]=='1'){
            v=max(v-5,0);
            ans+=v;
        }
        else {
            if(v<10) continue;
            ans+=(v-10);
        }
    }
    cout<<ans<<endl;
    return ;
}
int main(){
    t=1;
    while(t--){
        solve();
    }return 0;
}

B

其实就是问一个长度为n的木块切割成最多长度大于等于t的小木块,一定有k个切割点,切割点占一个长度

贪心:

尽可能的切割更多,所以每个小木块保持最短t

如果可以切割成小木棍数量多于k个切割点,那么最多可以k+1个棍子

否则尽可能的切割成(n-k)/t个,多余的切割点放在最后

点击查看代码
#include<bits/stdc++.h>

using namespace std;

int _;
int n,t,k;
void solve(){
  	cin>>n>>t>>k;
	int cnt=(n-k)/t;
    if(cnt>k){
        cnt=k+1;   
    }
    cout<<cnt<<endl;
}
int main(){
    cin>>_;
    while(_--){
        solve();
    }return 0;
}

I

玄学猜测题

有时候不能理解这样的题是为了什么,考验提交代码的勇气?

当然可能是数学直觉

点击查看代码
#include<bits/stdc++.h>

using namespace std;

int t;
int n,m;
void solve(){
    cin>>n>>m;
    if(n==0 || m==0){
        if(m!=n) puts("No");
        else puts("Yes");
        return ;
    }
    else puts("Yes");
}
int main(){
    cin>>t;
    while(t--){
        solve();
    }return 0;
}
#E

做法开个数组统计行列的X棋子数量,同行行列有O让数量为负,代表无法连成三个

  1. 很显然,如果只下了1或者2步棋,必胜

  2. 如果只下了3步棋,那么输的情况只有类似于

  • 空了一行或一列,且其他行如一下排列的棋局
    (样例给的不错

    OXX , OXO

    XOO , XOX

    GGG , GGG

只要确保某行某列某斜线棋子数量一定是2就行

  • 如果空的是斜线,那么分为两种情况

    • X在另一个斜线上,且周边没有X,不赢

      GOX

      XGO

      OXG

    • X在另一个斜线上,周边有X,必赢
      还是一个道理只要确保某行某列某斜线棋子数量大于1就行

  • 那么再考虑,空的不连续的情况,只需要验证,某一行还是某一列,还是某个斜线有一个X就可以了,且没有O,即大于等于1

3.下了4步棋,只需要找到某行某列某斜线数量为2就行

所以只需要找棋子数量>0就行

点击查看代码

#include<bits/stdc++.h>

using namespace std;
#define ll long long
int t;
map<int,int> mp[5];
void solve(){
    int cnt=0;
    mp[1].clear();mp[2].clear();
    for(int i=1;i<=3;++i)
        for(int j=1;j<=3;++j){
            char p;cin>>p;
            if(p=='G') continue;
            ++cnt;
            mp[1][i]+=(p=='X')?1:-5;
            mp[2][j]+=(p=='X')?1:-5;
            if(i+j==4)mp[1][i+j]+=(p=='X')?1:-5;
            if(i-j==0)mp[1][i-j]+=(p=='X')?1:-5;
        }
    bool book=0;
    int res=0;cnt/=2;
    for(int i=1;i<=3;++i)
        for(auto [x,y]:mp[i])
             if(y>0) book=1;
    if(cnt<=2) puts("Yes");
    else {
        if(book)puts("Yes");
        else puts("No");
    }
}
int main(){
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

C

很容易知道是贪心,但是怎么去贪,才是这个题的难点

而且题目已知,交换次数和取反的次数与最终的值有关,而这里有两种方式,我们则需要尽量睿智地选择一种方式

  • 2xy时,这时,只取反肯定是最好的选择
  • 否则尽量选择交换

讨论交换的情况:

对于a,b异或与c相同的,我们不予考虑
对于a,b异或与c不同的,我们发现有四种情况

  • a:0,b:0
  • a:0,b:1
  • a:1,b:0
  • a:1,b:1

我们发现任意两两之间,都可以选择a或b交换,可以使得异或正确(可以试着枚举一下

这时,

  • 如果其中一种情况大于其他三种,那么就可以用该种情况与其他的两两交换,剩下的就取反
  • 否则,就两两之间相互抵消,如果是奇数,最后剩下的一个取反
点击查看代码
#include<bits/stdc++.h>

using namespace std;
#define ll long long 
string a,b,c;
int n;
const int maxn=1e6+10;
int x,y;
ll cnt[5];

int main(){
    cin>>n>>x>>y;
    cin>>a>>b>>c;
    for(int i=0;i<n;++i){
        if(c[i]==(a[i]==b[i]?'0':'1')) continue;
        cnt[0]+=(a[i]=='0' && b[i]=='0');
        cnt[1]+=(a[i]=='0' && b[i]=='1');
        cnt[2]+=(a[i]=='1' && b[i]=='0');
        cnt[3]+=(a[i]=='1' && b[i]=='1');
    } 
    sort(cnt,cnt+4);
    ll sum=0;
    for(int i=0;i<4;++i) sum+=cnt[i];
    ll ans=(sum)*x;
    if(cnt[3]>sum-cnt[3])//一个情况大于另外三种
        ans=min(ans,(ll)(sum-cnt[3])*y+(2*cnt[3]-sum)*x);    
    else {
        ans=min(ans,(ll)sum/2*y+sum%2*x);
    }
    cout<<ans<<endl;
    return 0;
}

L

构造,找一个三元组,恰好两对互质,且每个数只能用一次

显然n<=3,无解

n>3 && n<6时,我们能找到最小元素组成的三元组是(1,2,4)

n>=6

有个常用结论:连续的数互质
为了保证三元组在n的范围内取到最多,所以从小开始取

我们可以找到x,x+1,x+3满足条件,而x+2,x+4,x+5也是满足条件的,所以这两个三元组是连续的6个数
此时x=1,7,..,6n+1,是奇数,可以推得上式正确

但这里需要注意,当n时,还会多出一组三元组

我们提前找出前9个数的合法三元组,这样后续的三元组就可以按照连续6个数的方式取
而这时的三元组,以x=10为开头,x=10,16,...,10+6n,是偶数,所以
x+1,x+2,x+5;x,x+3,x+4是正确的连续三元组

点击查看代码
#include<bits/stdc++.h>

using namespace std;
#define int long long 
int t;
int n;
void solve(){
    cin>>n;
    if(n<=3){
        cout<<0<<endl;
        return ;
    }
    cout<<n/3<<endl;
    
    if(n<6)
    {
        cout<<"2 3 4"<<endl;
        return;
    }
    else {
        
        if((n/3)%2==0){
            for(int i=1;i+5<=n;i+=6){
                cout<<i<<" "<<i+1<<" "<<i+3<<"\n";
                cout<<i+2<<" "<<i+4<<" "<<i+5<<"\n";
            }
        }
        else {
            puts("1 2 4");
            puts("3 9 5");
            puts("6 8 7");
            for(int i=10;i+5<=n;i+=6){
                cout<<i+1<<" "<<i+2<<" "<<i+5<<"\n";
                cout<<i<<" "<<i+3<<" "<<i+4<<"\n";
            }
        }
    }
}
signed main(){
    cin.tie(0);
    cout.tie(0);
    cin>>t;
    
    while(t--){
        solve();
    }
    return 0;
}

D

区间内,可0变1,1变0,且可以任意排列
所以一个长度为kk>1的区间,可以等效为00,11,01,10等四种情况

所以在排列每个区间时,为了尽可能减小对答案的贡献,我们可以使区间衔接处相等(一定可以,因为0和1之间可以转化),而只有区间内不同时则一定会对答案贡献+1

因此,只需要统计区间内是否全部相同就能判断对答案的贡献

注意,如果全部相同,那贡献为1

点击查看代码
#include<bits/stdc++.h>

using namespace std;
int n;
int ans=0;
string s;
const int maxn=1e6+10;
int pre[maxn];

int main(){
    cin>>n;
    cin>>s;s=" "+s;
    for(int i=1;i<=n;++i) pre[i]=pre[i-1]+(s[i]=='1');
    for(int k=1;k<=n;++k){
        int cnt=1;
        for(int i=1;i<=n;i+=k){
            int l=i,r=min(n,l+k-1);
            cnt+=(pre[r]-pre[l-1]<r-l+1 && pre[r]>pre[l-1]);
        }
        ans^=cnt;
    }
    cout<<ans<<endl;
}

本文作者:归游

本文链接:https://www.cnblogs.com/guiyou/p/18705407

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   归游  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起