2026年寒假牛客训练赛补题(一)

0 阅读5分钟

 写在前面

笔者目前处于大二寒假期间,参加牛客寒假训练赛,水平应该在参与该训练赛成员的中上等,每一场比赛都会把我能补但是赛时没出的题补掉,今天是第一场训练赛

这场我决定补两个题,分别是思维、贪心

本篇博客题面和题解皆来自牛客训练赛:ac.nowcoder.com/acm/contest…

B

题面:

赛时没开出来的原因是误解题意,读题的时候其实注意到了,一张牌如果没有用的话,会保留下来,但是思考的时候误认为一张牌只能用一次,然后越想越复杂,最后没做出来

正解:

将a数组的数字从大到小排序,如果b数组有比a数组的数字更小的牌,那么无论这张牌放在哪里,比它大的牌一定可以获取一分(因为小牌出不去会一直保留)。

因为a一张牌最多获得一分,且这一分是谁给的不重要,所以只要关注b数组牌面最小的一张牌即可。换句话说,如果a数组的牌大于b数组牌面最小的一张牌,那么这张牌一定可以获取分数。

因此需要统计a数组中大于minb的数量和小于minb的数量,记为numd和numx。

现在只剩一个限制了,那就是minb不能被消除,即小于minb的牌绝对不能在没有让minb完全发挥作用前和minb配对。所以需要将大于minb的数放在小于minb的数之前

而这两部分内部可以随意排列,最后的结果就是两部分内部排列种数的乘积

注意在过程中要取模

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD=998244353;

void solve()
{
	ll n;
	cin>>n;
	vector<ll>a(n);
	vector<ll>b(n);
	for(auto &t:a) cin>>t;
	for(auto &t:b) cin>>t;
	auto temp=min_element(b.begin(),b.end());
	ll minb=*temp;
	ll numd=0,numx=0;
	for(auto t:a)
	{
		if(t>minb) numd++;
		else numx++;
	}
	ll ans=1;
	for(int i=1;i<=numd;i++)
	{
		ans=(ans*i)%MOD;
	}
	for(int i=1;i<=numx;i++)
	{
		ans=(ans*i)%MOD;
	}
	cout<<ans<<endl;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

G

题面:

这个题本质上是很简单的,但是赛时思考过于混乱,将很多同种情况分成不同情况考虑,最终乱得实在写不出来了

先附上笔者赛时复杂思路:

先比较l和r的位数

若l位数小于r,则只需要关注r

把每一位提取出来

若全部是9直接毕业

若首位不是1,且后边有不是9的数,将首位-1,其余位置全变成9

若首位是1,且后边有不是9的数,则从前往后找到第一个不是0的数,将其-1.这一位后的所有数变成9

若后边没有不是9的数,直接翻转就行

若l位数和r相等,则从前往后依次比较每一位大小,直到找到r比l大的那一位,然后前边的不动,这一位扣1,后边变9

若找到的这一位后边的已经全是9了,就不需要操作直接反转,如果直到最后一位才找到,也直接反转

若最终操作后有前导0,则去除前导零

这个思路最后通过 30%的样例

其实这个思路大概方向是对的,但是太混太乱了,而且没有这么复杂,很多情况应该归为一类情况

正解:

明显的,想要使折叠数最大,那么要针对R修改:从后往前,一定要保证9最多且位数最长

根据配9的难易程度可以分为两种情况:

一是配9不需要掉位,一是配9需要掉位

配9需要掉位的只有一种情况:100000000……0000,这种情况下折叠数是1,所以必须掉位变成99999……9999,此时若L!=R,与L是几无关,若L=R,那么直接输出1就行

剩余的都是不需要掉位的情况。那么L位数如果小于R,这个界限将毫无用处,所以需要将L视作与R位数相同的10000……0000,如果L位数等于R,那么正常进行下一步即可

现在需要将L和R从高位到低位遍历,如果R这一位高于L,那么可以将这一位扣1,后续全部变成9,这样结果必然是最优的,不过需要注意的是,若这一位后的已经全部是9,这一位可以直接保留

将最终得到的数反转就是答案

有一个很关键的地方是要去除前导零

代码 :

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

void solve()
{
	ll l,r;
	cin>>l>>r;
	string L=to_string(l);
	string R=to_string(r);
	ll numl=L.size();
	ll numr=R.size();
	if(R[0]=='1')
	{
		bool tiqian=true;
		for(int i=1;i<R.size();i++)
		{
			if(R[i]!='0') tiqian=false;
		}
		if(tiqian==true&&numl<numr)
		{
			cout<<r-1<<endl;
			return;
		}
		if(tiqian==true&&numl==numr)
		{
			cout<<1<<endl;
			return;
		}
	}
	if(numl<numr)
	{
		L.clear();
		L.push_back('1');
		for(int i=0;i<numr-2;i++) L.push_back('0');
		L.push_back('1');
	}
	for(int i=0;i<numr;i++)
	{
		if(R[i]!=L[i])
		{
			bool tiqian=true;
			for(int j=i+1;j<numr;j++)
			{
				if(R[j]!='9') tiqian=false;
			}
			if(tiqian==true)
			{
				reverse(R.begin(),R.end());
				cout<<R<<endl;
				return;
			}
			else
			{
				for(int j=i+1;j<numr;j++)
				{
					R[j]='9';
				}
				R[i]--;
				reverse(R.begin(),R.end());
				cout<<R<<endl;
				return;
			}
		}
	}
	reverse(R.begin(),R.end());
	for(int i=0;i<R.size();i++)
	{
		if(R[i]!='0')
		{
			for(int j=i;j<numr;j++) cout<<R[j];
			cout<<endl;
			return;
		}
	}
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

篇末总结

这两道题,我都是具备赛时开出来的能力的,一方面是太长时间没写题了,一方面是能力仍有欠缺,思考不够全面不够宏观,还有就是题意理解错误

这场最后我的成绩是六道,最后大概是1500名左右,我已经相对满意了