【2022牛客寒假训练营1】题解(除G和K)

150 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


训练营链接 :
ac.nowcoder.com/acm/contest…

报名链接:
ac.nowcoder.com/order?itemI…


本人水平有限,文中难免有所错误。时间有限,后续题解会尽量写。


A. 九小时九个人九扇门

数字根:一个数字的数字跟为该数字各个位数之和,对于这个结果 再对其各个位数求和,直到求出来的结果为一个数字,即小于10,最终得出的结果为初始数字的数字根。
n个人,每个人都有一个数字,不同的人可以组合在一起。共标有1-9号数字的9扇门,只有数字跟和门上数字相等才能打开门,求开每种门的所有组合情况数。

数字根的几个性质

先对数字根分析: 对于两个数xy,可以发现 x+y的数字根==·x的数字根+y的数字根

也就是说,数字根的计算顺序并不影响最终得出的数字根的结果

那么就可动态规划:

状态表示:

f[i][j]f[i][j]:前i个人,数字根为j的组合情况

状态转移:

count()函数是计算数字根的函数

不选中第i个人时:f[i][j]=f[i1][j]f[i][j] = f[i-1][j] (注意要先转移所有的不选中的,再进行下面的操作)

选中第i个人时:f[i][count(j+a[i])]+=f[i1][j]f[i][count(j+a[i])] += f[i-1][j]

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+5,mod = 998244353;

ll a[N],f[N][10];
ll count(ll x)
{
	while(x>=10)
	{
		int cnt = 0;
		while(x)
		{
			cnt += x % 10;
			x /= 10;
		}
		x = cnt;
	}
	return x;
}

void solve()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) 
	{
		ll x;
		scanf("%lld",&x);
		a[i] = count(x);
	}
	//初始化
	for(int i=0;i<=n;i++) f[i][0] = 1;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=9;j++) f[i][j] = f[i-1][j];
		for(int j=0;j<=9;j++) f[i][count(j+a[i])] =(f[i][count(j+a[i])] + f[i-1][j]) % mod;
	}
	for(int i=1;i<=9;i++) printf("%lld ",f[n][i]);
}
int main()
{
	int t;
	t = 1;
	while(t--) solve();
 	return  0;
 } 

B. 炸鸡块君与FIFA22

本题是ST表问题
了解ST表请点击 :blog.csdn.net/qq_50285142… 初始化

状态表示:

f[k][i][j]f[k][i][j]表示在初始分数为𝑘的情况下经历了[𝑖,𝑖+2𝑗1][𝑖, 𝑖 + 2^𝑗 − 1]一段游戏后分数的变化量,k要在模三的情况下

状态转移:

f[k][i][j]=f[k][i][j1]+f[(k+f[k][i][j1])%3][i+(1<<j1)][j1]f[k][i][j] = f[k][i][j-1] + f[(k+f[k][i][j-1])\%3][i+(1<<j-1)][j-1]

[𝑖,𝑖+2𝑗1][𝑖, 𝑖 + 2𝑗 − 1]分成两个区间[i,i+2j11],[i+2j1,i+2j1][i,i+2^{j-1}-1],[i+2^{j-1},i+2^j-1],这两个区间长度相同都为2j12^{j-1}

第一个区间分数变化为f[k][i][j1]f[k][i][j-1],那么第二个区间要在这个变化的基础上进行计算,故为(k+f[k][i][j1])%3(k+f[k][i][j-1])\%3

计算询问

最后计算一个区间[l,r][l,r]的变化量时,一个区间的长度可以由若干个2的次幂加和组成,即从ll跳到rr可以跳若干个2的次幂,2的次幂的获取刚好可以由区间长度的二进制表示形式来获得。

则对于一次询问,可以先将其初始分数𝑠3取模,然后按照倍增的套路从𝑙跳若干个2的次幂跳到𝑟,跳的时候要按照分数对3取模的结果来决定访问哪个f

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;

int f[3][N][20];

int main()
{
	int n,m;
	cin>>n>>m;
	string s;
	cin>>s;
	for(int i=0;i<n;i++)
	{
		if(s[i]=='W') f[0][i][0] = f[1][i][0] = f[2][i][0] = 1;
		if(s[i]=='L') f[1][i][0] = f[2][i][0] = -1;
	 } 
	
	for(int j=1;(1<<j)<=n;j++)
		for(int i=0;i+(1<<j)-1<n;i++)
			for(int k=0;k<3;k++)
				f[k][i][j] = f[k][i][j-1] + f[(k+f[k][i][j-1])%3][i+(1<<j-1)][j-1];
	
	while(m--)
	{
		int l,r,v;
		cin>>l>>r>>v;
		l--,r--;
		int len = r - l + 1;
		for(int i=0;len;i++)
		{
			if(len & 1) 
			{
				v += f[v%3][l][i];
				l += (1<<i);
			}
			len >>= 1;
		}
		cout<<v<<endl;
	}
	return 0;
}

C Baby’s first attempt on CPU

cnt[i]表示第i个语句后面有多少个空语句

从前往后循环就可以了,基本上就是模拟。哪里不满足就加空语句,空语句优先加和本句相邻的

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

int a[105][4];
int cnt[105];

void solve()
{
	int n;cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=3;j++)
			cin>>a[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=3;j++)
		{
			if(a[i][j])
			{
				if(j==1) cnt[i-j] += 3;
				else if(j==2 && cnt[i-j]+cnt[i-j+1]<2) cnt[i-1] += 2-(cnt[i-j]+cnt[i-j+1]);
				else if(j==3 && cnt[i-j]+cnt[i-j+1]+cnt[i-j+2]<1) cnt[i-1] +=1;
			}
		}
	int res = 0;
	for(int i=1;i<=n;i++) res += cnt[i];
	cout<<res<<endl;
}
int main()
{
	int t;
	t = 1;
	while(t--) solve();
	return  0;
 } 

D 牛牛做数论

首先用到了欧拉函数的定义:

φ(n)=n i=1s(11pi)\begin{aligned} \varphi(n) &=n~ \prod_{i=1}^{s} (1- \frac{1}{p_i}) &\end{aligned},pip_i代表n能够分解的所有的质因数的种类,例如12,则pip_i可以表示2,32,3

H(x)=φ(x)x=i=1s(11pi)=i=1spi1piH(x) = \frac{\varphi(x)}{x}=\prod_{i=1}^{s} (1- \frac{1}{p_i})=\prod_{i=1}^s \frac{p_i-1}{p_i}

最小值:

可以发现取一个质因数,就乘上一个小于1的分数,是越乘越小。如果能取遍所有种类的质因子那么将会变得最小。

所以答案为2、2×3、2×3×5、2×3×5×7......这些前若干个素数的积中,最大的且不超过n的那一个,如n=233,则答案为2×3×5×7=210

最大值: 对于素数有φ(x)=x1\varphi(x) = x-1,则H(x)=x1xH(x) = \frac{x-1}{x},当然是素数越大,结果越大。

所以答案为不超过n的最大素数。

注意:

  • 对问题一,因为很少的一些素数积就会超过 10910^9 了,因此预处理出前几十个素数即可,小心溢出问题

  • 对问题二,从n开始依次递减地暴力判断是否为素数即可,这是因为10^9以内最大的两个素数间隔是282,所以这样做最多只要判断282个数字,且判断时往往跑不满根号的复杂度。

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

int primes[] = {0,2,3,5,7,11,13,17,19,23,29};


bool isprime(int x)
{
	if(x<=3) return true;
	for(int i=2;i * i <= x ;i++)
		if(x % i == 0 ) return false;
	return true;
}
void solve()
{
	int n;cin>>n;
	if(n==1) cout<<-1<<endl;
	else
	{
		ll res1=1,i=1;
		while(res1*primes[i] <= n)
		{
			res1 *= primes[i];
			i ++;
		}
		ll res2 = n;
		while(!isprime(res2)) res2 --;
		cout<<res1<<" "<<res2<<endl;
	}
}
int main()
{
	int t;
	cin>>t;
	while(t--) solve();
	return  0;
 } 

E 炸鸡块君的高中回忆

第一次可以带进去m个人,接下来的每一次都可以带进去m-1个人。
注意特判n==m

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+5;

void solve()
{
	ll n,m;
	cin>>n>>m;
	ll res = 0;
	if(n==m)
	{
		cout<<1<<endl;
		return;
	}
	n -= m;
	res += 1;
	if(m-1 == 0) cout<<-1<<endl;
	else
	{
		res += (n+m-2)/(m-1)*2;
		cout<<res<<endl;
	}
}
int main()
{
	int t;
	cin>>t;
	while(t--) solve();
 	return  0;
 } 

F 中位数切分

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+5,mod = 998244353;

int a[N];

void solve()
{
	int n,m;
	scanf("%d%d",&n,&m);
	int cnt = 0;
	for(int i=1;i<=n;i++) 
	{
		scanf("%d",&a[i]);
		if(a[i] < m) cnt++;
	}
	if(2*cnt+1 > n) cout<<-1<<endl;
	else
	{
		int res = 1;
		cout<<n-cnt*2<<endl;
	}
}
int main()
{
	int t;
//	cin>>t;
	scanf("%d",&t);
//	t = 1;
	while(t--) solve();
 	return  0;
 } 

H 牛牛看云

又懒了
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+5;

ll pre[N],a[N];

void solve()
{
	int n;cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++) pre[i] = pre[i-1] + a[i];
	ll res = 0;
	for(int i=1;i<=n;i++)
	{
		res += abs(2*a[i] - 1000);
		int pos = lower_bound(a+1,a+1+i-1,1000-a[i])-a;
		res += (pos-1) * (1000-a[i] )- pre[pos-1];
		res +=  (pre[i-1]-pre[pos-1]) -(i-pos) * (1000-a[i] );
	}
	cout<<res<<endl;
}
int main()
{
	int t;
//	cin>>t;
	t = 1;
	while(t--) solve();
 	return  0;
 } 

I B站与各唱各的

直接照搬官方题解了,就是懒

答案即为:𝑚×2𝑛22𝑛𝑚 × \frac{2^𝑛 − 2 }{2^𝑛}

• 首先注意到句子与句子之间没有办法互相影响,因此答案是一句话的期望乘以𝑚;

• 由于无法交流,每个人在唱每句时唯一的策略就是随机以𝑝𝑖的概率决定唱或不唱这一句(对超算就是拿来生成随机数的,超算好憋屈);

• 于是失败的概率即为𝑝𝑖+(1𝑝𝑖)\prod𝑝_𝑖 + \prod(1 − 𝑝_𝑖),我们要最小化这个式子,根据一些高中或带学数学的分析,可以得出当𝑝1=𝑝2==𝑝𝑛𝑝_1 = 𝑝_2 = ⋯ = 𝑝_𝑛时,该式子取最小值,此时每句唱成功的概率计算可以得到,是2𝑛22𝑛\frac{2^𝑛 − 2 }{2^𝑛}

• 不会逆元的同学赶快去学呀,这种推个式子输出逆元的题很常见的!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int N = 2e5+5,mod = 1e9+7;

ll fast(ll a,ll b)
{
	ll res = 1;
	while(b)
	{
		if(b&1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1; 
	}
	return res;
}
ll inv(ll x)
{
	return fast(x,mod-2);
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		ll n,m;
		cin>>n>>m;
		ll res = (fast(2,n)-2) * inv(fast(2,n)) % mod * m % mod;
		cout<<res<<endl;
	}
	return 0;
}

J 小朋友做游戏

闹腾的人最多n2\frac{n}{2}个,那么安静小朋友个数满足cnt1nn2cnt1 \geq n- \frac{n}{2},先把至少的nn2n-\frac{n}{2}取了,然后对剩下的从大到小排序,从大往小取就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+5;

void solve()
{
	int x,y,n;
	cin>>x>>y>>n;
	vector<int>a(x+1),b(y+1);
	for(int i=0;i<x;i++) cin>>a[i];
	for(int i=0;i<y;i++) cin>>b[i];
	sort(a.begin(),a.end(),greater<int>());
	sort(b.begin(),b.end(),greater<int>());
	if(n-n/2>x ) cout<<-1<<endl;
	else
	{
		vector<int>tmp;
		for(int i=n-n/2;i<x;i++) tmp.push_back(a[i]);
		for(int i=0;i<y;i++) tmp.push_back(b[i]);
		sort(tmp.begin(),tmp.end(),greater<int>());
		ll res = 0;
		int cnt = n-n/2;
		for(int i=0;i<n-n/2;i++) res += a[i];
		for(int i=0;i<tmp.size() && cnt<n ;i++,cnt++)
			res += tmp[i];
		cout<<res<<endl;
	 } 
}
int main()
{
	int t;
	cin>>t;
//	t = 1;
	while(t--) solve();
 	return  0;
 } 

L 牛牛学走路

简单模拟题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+5;

double dis(int x,int y)
{
	return sqrt((double)(x*x) + (double)(y*y));
}
void solve()
{
	int n;cin>>n;
	string s;
	cin>>s;
	double res = 0;
	int x = 0,y = 0;
	for(int i=0;i<n;i++)
	{
		if(s[i]=='L') x--;
		else if(s[i]=='R') x++;
		else if(s[i]=='U') y++;
		else y--;
		
		res = max(res,dis(x,y));
	}
//	cout<<res<<endl;
	printf("%.12lf\n",res);	
}
int main()
{
	int t;
	cin>>t;
//	t = 1;
	while(t--) solve();
 	return  0;
 }