牛客周赛79题解
A.小红的合数查找
小红拿到了一个正整数
,她希望你在
区间内找到一个合数,你能帮帮她吗?
一个数为合数,当且仅当这个数是大于
的整数,并且不是质数。
因为除了2意外的所有偶数都是合数,所以可选的答案要么是要么是
,所以特判一下1和2再根据
的奇偶性输出答案即可
# Python
def solve():
x = int(input())
if x == 1:
print(-1)
elif x == 2:
print(4)
elif x % 2 == 1:
print(x + 1)
else:
print(x)
B.小红的小球染色
有
个白色小球排成一排。小红每次将随机选择两个相邻的白色小球,将它们染成红色。小红将持续这个操作直到无法操作,请你计算小红操作次数可能的最小值和最大值。
很显然最大操作是,最小操作是
,特判一下几个比较小的数的情况防止出错
// C++
void solve(){
int n;
std :: cin >> n;
if(n == 2) {std :: cout << 1 << " " << 1 << endl ; return;}
else if(n == 3) {std :: cout << n / 3 << " " << n / 2 << endl ; return;}
int m = n / 3 , mo = n % 3;
std :: cout << m + (mo >= 2) << " " << n / 2 << endl;
}
C.小红的二叉树
小红想知道,深度为
的满二叉树
有多少条长度为
的简单路径
?由于答案可能很大,请将答案对
取模后输出。
在本题中,两条简单路径所包含的点集不同时,被视为不同的。例如,路径
与路径
被视为相同的,因为它们均包含点
与点
。
一棵深度为
的满二叉树
由恰好
个节点组成,每一个节点要么是叶子节点,要么有
个儿子,并且全部叶子节点的深度均为
。
简单路径
是指这样一条路径,其经过的顶点和边互不相同。
考虑简单路径的中点,发现除了根节点意外的所有非叶子节点都可以连出3条路,即答案为
// C++
const int mod = 1e9 + 7;
int quick_power(int a,int b,int mod){
int ans=1;
while(b){
if(b%2==1) ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
void solve(){
int n;
std :: cin >> n;
int ans = 3 * (quick_power(2 , n - 1, mod) - 2);
ans = (ans + 1) % mod;
if(n == 1) std :: cout << 0 << endl;
else std :: cout << ans << endl;
}
D.小红的质数寻找
小红拿到了一个正整数
,她希望你在
区间内找到一个正整数,满足该正整数所有数位之和为一个质数,你能帮帮她吗?
发现这道题的数据范围足足有,那就完全不用考虑计算了,这是一道构造题
我们假设其中a为第一位数字,C为后续部分,很显然答案的第一位可以取到
的所有情况,对9种数字分析发现无论如何这个范围里都包含数位和为素数的数,那么只要随便输出一个范围内的素数然后跟上n-1个0即可
// C++
void solve(){
std :: string s;
std :: cin >> s;
int l = s[0] - '0' + 1 , r = (l - 1) * 2;
for(int i = l;i <= r;i ++){
if(i == 2 || i == 3 || i == 5 || i == 7 || i == 11){
std :: cout << i;
break;
}
}
for(int i = 1;i < num.size();i ++)std :: cout << 0;// 补0
std :: cout << endl;
}
E.小红的好排列
小红认为一个偶数长度为
的排列
是好排列,当且仅当恰好有一半的
使得
是
的倍数。
小红想知道,全部长度为
的排列***有多少个好排列?由于答案可能很大,请将答案对
取模后输出。
长度为
的排列是由
这
个整数、按任意顺序组成的数组,其中每个整数恰好出现一次。例如,
是一个长度为
的排列,而
和
都不是排列,因为前者存在重复元素,后者包含了超出范围的数。
假设所和所有
都错开,那就可以得到
个题目要求的数,所以只要这个数不足一半就都是无解的
我们设,我们需要有d个位置同时满足
和
,这样才能把数量减少到
,此时有
个方案,再把3的倍数填进去有
个方案,把剩余的3的倍数位置用非3的倍数填满有
种方案,剩余的数随机填入剩余位置有
种方案,全部乘起来即可
// C++
const int mod = 1e9 + 7;
// 排列组合类
struct Comb {
const int P = 13331;
int qmi(int a, int b) {
int ans = 1;
while (b) {
if (b & 1)
ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
};
void init(int M) {
fac.resize(M + 10, 1);
inv.resize(M + 10, 1);
finv.resize(M + 10, 1);
for (int i = 2; i <= M; i++)
fac[i] = fac[i - 1] * i % mod;
finv[M]=qmi(fac[M],mod-2);
for(int i=M-1;i>=0;i--){
finv[i]=finv[i+1]*(i+1)%mod;
}
inv[1] = qmi(P, mod - 2);
for (int i = 2; i <= M; i++) {
inv[i] = inv[i - 1] * P % mod;
}
};
int C(int n, int m) {
assert(n>=0 && m>=0 && n<=m);
if (n == 0 || m == 0)
return 1ll;
int c = fac[m - n] * fac[n] % mod;
return fac[m] *finv[n]% mod * finv[m-n] % mod;
};
int A(int n, int m) { return fac[n] * C(n, m) % mod; }
std :: vector<int> fac, inv,finv;
};
void solve(){
Comb T;
int n;
std :: cin >> n;
T.init(1000000);
if(n / 3 * 2 < n / 2){
std :: cout << 0 << endl;
return;
}
int d = n / 3 * 2 - n / 2;
int ans = 1;
ans = ans * T.C(d , n / 3) % mod;
ans = ans * T.A(d , n / 3) % mod;
ans = ans * T.A(n / 3 - d , n - n / 3) % mod;
ans = ans * T.A(n - n / 3 , n - n / 3) % mod;
std :: cout << ans << endl;
}
F.小红的小球染色期望
有
个白色小球排成一排。小红每次将随机选择两个相邻的白色小球,将它们染成红色。小红将持续这个操作直到无法操作,请你计算小红操作次数的期望。
这是一道动态规划,定义表示长度为i的期望,我们先考虑第一次操作,枚举第一次操作的左端点,发现一共有
种可能,每种可能的概率都为
此时我们发现小球被第一次操作分成了左右两个部分,左部的长度范围是0到n-2,右部的长度范围是n-2到0,而他们各自的期望在动态规划的过程中已经被求出来了,所以期望操作数为:
// C++
const int mod = 1e9 + 7;
int quick_power(int a,int b,int mod){
int ans=1;
while(b){
if(b%2==1) ans=ans*a%mod;
a = a * a % mod;
b /=2;
}
return ans;
}
void solve(){
int n;
std :: cin >> n;
std :: vector<int> dp(n + 1);
std :: vector<int> pre(n + 1);
dp[2] = 1;
pre[2] = 1;
for(int i = 3;i <= n;i ++){
int p = quick_power(i - 1 , mod - 2 , mod);//求出分母逆元
dp[i] = (pre[i - 2] * 2 % mod * p % mod + 1) %mod;
pre[i] = (pre[i - 1] + dp[i]) % mod;
}
std :: cout << dp[n] << endl;
}