2021牛客寒假算法基础集训营1-补题

A - 串

解法1:设置三状态dp,根据状态转移式递推求解

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
ll dp[N][3];
int main () {
    int n; cin >> n;
    // 定义dp状态
    // dp[i][0] : 无us子序列的长度为i的字符串的数量
    // dp[i][1] : 有u但是无us的长度为i的字符串的数量
    // dp[i][2] : 有us子序列的长度为i的字符串的数量
    dp[1][0] = 25;
    dp[1][1] = 1;
    dp[1][2] = 0;
    ll ans = 0;
    // 状态转移
    for (int i = 2; i <= n; i++) {
        //在前i-1个字符无us的基础上 + 除u以外的任意字符(25个)
        dp[i][0] = (dp[i - 1][0] * 25) % mod;
        //在前i-1个字符无us的基础上 + 一个u字符(1个)
        //或在前i-1个字符有u无us的基础上 + 除s以外的任意字符(25个)
        dp[i][1] = (dp[i - 1][0] * 1 + dp[i - 1][1] * 25) % mod;
        //在前i-1个字符有u无us的基础上 + 一个s字符(1个)
        //或在前i-1个字符有us的基础上 + 任意字符(26个)
        dp[i][2] = (dp[i - 1][1] * 1 + dp[i - 1][2] * 26) % mod;
        // 累加各长度的数量
        ans = (ans + dp[i][2]) % mod;
    }
    cout << ans << endl;
    return 0;
}

解法2
组合数求解,反向推导
先求不存在us的情况,由 所有情况 - 不存在us的情况 得到解
不存在的情况:
1)没有s, 则必定不存在us,pow(25, n)
2)有s,且s前面没u,则必定不存在us
倒数第一位是s,其他位置不能是u,pow(25, n - 1)
倒数第二位是s,s前面的位置不能是u,s后面考虑到和之前的情况冲突,不能是s, pow(25, n - 1)
倒数第三位是s,s前面的位置不能是u,s后面依旧不能是s,pow(25, n - 1)
……
第一位是s,s后面均不能为s,pow(25, n - 1)
所以:pow(25, n - 1) * n

不存在us的总结果: pow(25, n) + pow(25, n - 1) * n
综合:ans = pow(26, n) - pow(25, n) - pow(25, n - 1) * n

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll qpow(ll a, int b) {
	ll ans = 1L;
	while (b) {
		if (b & 1) ans = (ans * a) % mod;
		a = (a * a) % mod;
		b >>= 1;
	}
	return ans;
}
int main() {
	int n; scanf("%d", &n);
	ll ans = 0;
	for (int i = 1; i <= n; i++) {
		ans = (ans + (qpow(26, i) - qpow(25, i - 1) * i - qpow(25, i)) % mod) % mod;
	} 
	cout << (ans + mod) % mod << endl;
	return 0;
}

B - 括号

解法1

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n; scanf("%d", &n);
	if (n == 0) {
		printf("(\n");
		return 0;
	}
	int l = (int) sqrt(n); 	// 左边
	for (int i = 0; i < l; i++) putchar('(');
	int r = n / l; 			// 右边 
	if (l * r == n) {
		for (int i = 0; i < r; i++) putchar(')');
		puts("");
	}
	else {
		int rem = n % l; // rem补充括号
		// 比如 7 = 2 * 3 + 1  : (())() 
		// 将一个左括号补充在倒数第一个右括号的左边,就能补充1对括号 
		for (int i = 0; i < r - rem; i++) putchar(')');
		putchar('('); // 补充 rem 对括号
		for (int i = 0; i < rem; i++) putchar(')');
		puts("");
	}
	return 0;
}

解法2

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n; scanf("%d", &n);
	int l = n / 50000; // n <= 100000, 直接对半分,暴力构造
	for (int i = 1; i <= l; i++) printf("(");
	int rem = n % 50000;
	for (int i = 1; i <= 50000; i++) {
		if (i == 50000 - rem + 1) printf("(");
		printf(")");
	}
	return 0;
}

I - 限制不互素对的排列
思路
由于每个排列种[n / 2]个偶数,如果k < n / 2是可以找到k+1个偶数,组成k对gcd>1,而如果k = = n / 2是只能找到k个偶数,找不到k+1个偶数的,但是对于6,是可以单独提出来配合3组成一对gcd>1的。
如果 k < n / 2,可以直接从2依次输出k+1个偶数(这k+1个数可以组成k对gcd>1),接着从1开始将其他数都输出即可;
如果 k == n / 2,可以直接从2依次输出k-1个偶数(不包括6),然后输出6和3,接着从1开始将其他数都输出即可。
特殊情况n < 6 && k = = n / 2 时无解(可以自行论证)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
bool vis[maxn];
int main() {
    int n, k; cin >> n >> k;
    if (n <= 5 && k == n / 2) cout << "-1" << endl;
    else {
        if (k == n / 2) {
            int x = 2;
            while (k--) {
                if (x != 6) {
                    cout << x << " ";
                    vis[x] = true;
                }
                x += 2;
            }
            cout << "6 3 ";
            vis[6] = vis[3] = true;
            for (int i = 1; i <= n; i++) {
                if (!vis[i]) cout << i << " ";
            }
            cout << endl;
        }
        else {
            k++;
            int x = 2;
            while (k--) {
                cout << x << " ";
                vis[x] = true;
                x += 2;
            }
            for (int i = 1; i <= n; i++) {
                if (!vis[i]) cout << i << " ";
            }
            cout << endl;
        }
    }
    return 0;
}

J - 一群小青蛙呱蹦呱蹦呱
思路
2将所有2^k去掉
3将所有3^k去掉
5将所有5^k去掉

保留下来的数字符合的条件为:具有两种以上不同的质因子
对保留下来的数求lcm,假定保留下来的数为
p1 ^ a1 * p2 ^ a2 * p3 ^ a3 … * pn ^ an (pi为质因子)
p1 ^ b1 * p2 ^ b2 * p3 ^ b3 … * pn ^bn
p1 ^ c1 * p2 ^ c2 * p3 ^ c3 … * pn ^ cn

则对他们求lcm,结果为 p1 ^ max(a1,b1,) * p2 ^ max(a2,b2,),* pn ^ max(an,bn,)
所以只需要求得各个质数pi的最大幂即可,即对a * pi ^ k <= n 求最大k (因为留下来的数至少需要两种不同质因子,所以a也是质数)。
对于 pi == 2时, a * 2 ^ k <= n, 为使得k最大,则应将a = 3,
所以有:3 * 2 ^ k <= n,即 k = log_2_[n / 3];
对于 pi > 2 时, a * pi ^ k <= n, 为使得k最大,则应将a = 2,所以有:2 * pi ^ k <= n,即 k = log_pi_[n / 2];
ps : 由于需要至少两种不同质数,所以 pi <= n / 2,且需要对2~n/2线性筛打表。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 8e7 + 1000;
//N不能等于n,数组开不下,题目质数 p <= n / 2, N = n / 2足够使用
const int mod = 1e9 + 7;
bool is_prime[N];
ll prime[N], cnt = 0;
void get_prime() { //不能传入n, n <= 1.6*10^8, n是有可能大于N的
    for (int i = 2; i < N; i++) { // 注意边界,不能等于N,避免数组越界
        if (!is_prime[i]) prime[cnt++] = i;
        for (int j = 0; j < cnt && i * prime[j] < N; j++) {
            is_prime[i * prime[j]] = true;
            if (i % prime[j] == 0) break;
        }
    }
}
int main() {
    int n; cin >> n;
    if (n < 6) {
        cout << "empty" << endl;
        return 0;
    }
    get_prime();
    // 3 * 2 ^ k <= n; k = log_2_[n/3];
    ll ans = 1, t = 1, p = 2;
    while (t * p <= n / 3) t *= p; 
    ans = (ans * t) % mod;
    // 2 * pi ^ k  <= n; k = log_pi_[n/2];
    for (int i = 1; prime[i] <= n / 2; i++) {
        p = prime[i], t = 1;
        while (t * p <= n / 2) t *= p;
        ans = (ans * t) % mod;
    }
    cout << (ans + mod) % mod << endl;
    return 0;
}

未完待续

上一篇:C++11一些常用的特性(稳定性和兼容性)


下一篇:throw与throw的区别