2026牛客寒假训练营1 | 部分题解

Hanzhi_OvO的头像 发布于 2026-02-05 131 次阅读


按照通过和赛后补题顺序:L、C、K、E、B、G、A、H、D。由于篇幅原因,这里提供了前五题。

L:Need Zero

根据题意,直接取出个位上的数字x,然后判断。由于x的范围只会在1到9中,对于所有偶数可以用5凑,对于5可以用2凑,其他的奇数用10凑。直接特判即可。

void solve()
{
    string s;
    cin>>s;
    char lst=s[s.size()-1];
    int lstn=int(lst-'0');
    if(lst=='0') cout<<"1";
    else
    {
        if(lstn%2==0) cout<<"5";
        else if(lstn==5) cout<<"2";
        else cout<<"10";
    }
}

C:Array Covering

不难发现,如果希望最大化答案,我们只需要让数组最大值maxn在选择的区间 (l, r) 中的一端点(可能maxn在l处,也可能在r处),选择区间的另一个端点拓展到数组的一端。经过两次这样的变换,除了整个数组首尾两头的元素都变成数组最大值maxn。

请注意题目中说是开区间,意味着答案并不是maxn*n,而是排除数组的首尾元素会发生变化,答案应该是(n-2)*maxn+a[0]+a[n-1]

void solve()
{
    ll n;
    cin>>n;
    vector<ll> a(n);
    for(int i=0;i<n;i++) cin>>a[i];
    if(n==1)
    {
        cout<<a[0]<<endl;
    }
    else
    {
        ll maxn=*max_element(a.begin(),a.end());
        ll ans=a[0]+a[n-1]+(n-2)*maxn;
        cout<<ans<<endl;
    }
}

K:Constructive

Ad-Hoc题,看到给出的样例开始思考是否和奇偶性有关。显然考场上我们没空给出严谨证明,但是通过样例就可以知道n=1有解,n=2无解。继续把玩,考虑n=3时的情况。我们知道,对于数组aNa_N中的所有元素都是正整数,求和的增长速度必然小于求积。所以我们从n的排列考虑(因为这是唯一可能让求和等于求积,不然剩下的情况只可能差距越来越大)。当n=3时,不难发现1 2 3这三个数正好满足求和等于求积。当拓展到n=4,4的排列不满足条件,显然,越往后越不可能满足条件。因此,只需要对输入进行n=1或3的特判,如果满足那么输出制定序列,不然无法构造。

void solve()
{
    int n;
    cin>>n;
    if(n==1) 
    {
        cout<<"YES"<<endl;
        cout<<1<<endl;
    }
    else
    {
        if(n==3) 
        {
            cout<<"YES"<<endl;
            cout<<"1 2 3"<<endl;
        }
        else cout<<"NO"<<endl;
    }
 
}

E:Block Game

如果对数据结构比较熟悉的话,看完题目就应该发现这就是一个循环队列(环状数组)。非常经典的“右端挤出去、左端插进来”。当然如果没有听说过也无妨,用deque双端队列能同样达到效果。

下面的代码是循环队列的解法,个人认为可以参考学习。deque思路类似,不再给出。

void solve()
{
    int n;
    ll k;
    cin>>n>>k;
    vector<ll> a(n);
    for(int i=0;i<n;i++) cin>>a[i];
    vector<ll> c(n+1);
    for(int i=0;i<n;i++) c[i]=a[i];
    c[n]=k;
    ll ans=LLONG_MIN;
    for(int i=0;i<=n;i++)
    {
        int j=(i-1+(n+1))%(n+1);
        ll sum=c[i]+c[j];
        ans=max(sum,ans);
    }
    cout<<ans<<endl;
}

B:Card Game

贪心、博弈背景,思维含量依旧在线。这道题具体讲讲心路历程。

最开始先读题,发现只需要对小苯的牌组进行排列,小红牌组无法更改顺序(这个也很关键,苯环讲题的时候有不少同学说没读出来这个含义)。认为优先的策略是田忌赛马,对两个卡组都sort一下,然后从左往右遍历对比a[i]与b[i],得到必胜牌数量,再计算答案。结果是本地跑了几组数据,WA了。

仔细想想,为什么田忌赛马不能解决这个题目?经过思考,我们构造了这组hack数据:

小苯:1、2、5

小红:3、4、6

如果按照上面的贪心思想,小苯在这个局面下没有得到一分。但是显然,我们可以用小苯的5对阵小红3,4中的一张牌,所以其实最少能得到1分。因此,我们需要改变贪心策略。

那么,干脆不改变小红的牌组顺序,对于小苯的牌组,根据上面的hack数据我们认识到,在这种局面下我们应该用“及时行乐”原则,把越大的牌越早出,直到剩下的牌无法盖过小红剩余牌组中的任何一张。

因此,对小苯的牌组进行降序排序,然后用双指针维护一个变量k,用来记录小苯手上的牌在本次游戏中最大能获得的分数。由于一张牌对应一分,所以也就是会有k张牌能被打出并移除。从性质上,小苯的整个牌堆分成可以打出和不可打出两种。方案数则为k!(n-k)!

void init() // 预处理阶乘
{
    f[0]=1;
    for(int i=1;i<=maxn;i++) f[i]=f[i-1]*i%modn;
}
 
void solve()
{
    int n;
    cin>>n;
    vector<ll> a(n),b(n);
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++) cin>>b[i];
    sort(a.begin(),a.end(),greater<ll>());
    int i=0,j=0,k=0;
    ll res=0;
    while(i<n&&j<n)
    {
        if(a[i]>b[j]) k++,i++;
        else j++;
    }
    res=f[k]*f[n-k]%modn;
    cout<<res<<endl;
}