牛客挑战赛73

112 阅读4分钟

比赛链接

ac.nowcoder.com/acm/contest…

img

S 老师的公式

输入一个数n[1, 1e6],求

gcd(i=1ni,i=1ni)\operatorname{gcd}\left(\sum_{i=1}^n i, \prod_{i=1}^n i\right)

我的思路,分为奇数和偶数两种情况

如果 n 是奇数,n + 1 一定是偶数, 且(n + 1) / 2 属于n!

如果 n 是偶数,n + 1 一定是奇数, 且 n / 2 属于n!, 但是n + 1不一定属于n!

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

// 获取一个数的所有质因数
vector<ll> getAllPrimeFactors(ll N) {
    vector<ll> result;
    for (int i = 2; i * i <= N; i++) {
        while (N % i == 0) {  // 如果 i 能够整除 N,说明 i 为 N 的一个质因子。
            result.push_back(i);  // 在这里添加 i,确保重复的质因数也被记录
            N /= i;
        }
    }
    if (N != 1) {  // 说明再经过操作之后 N 留下了一个素数
        result.push_back(N);
    }
    return result;
}

void solve(ll n) {
    // 计算1+2+...+n
    ll sum = n * (n + 1) / 2;
    if (n % 2 == 0) {
        // 如果 n 是偶数,n + 1 一定是奇数, 且 n / 2 属于n!, 但是n + 1不一定属于n!
        // 所以我们需要找到 n + 1 的所有质因数,然后去掉 n / 2 和 n + 1
        vector<ll> v = getAllPrimeFactors(n + 1);
        ll tm = 1;
        for (auto it: v) {
            if (it == n / 2 || it == n + 1) continue;
            tm *= it;
        }
        cout << n / 2 * tm << endl;
    } else if (n % 2 == 1) {
        // 如果 n 是奇数,n + 1 一定是偶数, 且(n + 1) / 2 属于n!
        // 所以答案是 (n + 1) / 2 * n
        cout << sum << endl;
    }
}

int main() {
    ll n;
    cin >> n;
    solve(n);
}

其他思路,

gcd(a,b)=gcd(a,bmoda)\operatorname{gcd}(a, b)=\operatorname{gcd}(a, b \bmod a)

不停取模得到答案

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
ll n;

ll gcd(ll a, ll b) {
    return b ? gcd(b, a % b) : a;
}

int main() {
    cin >> n;
    ll a = n * (n + 1) / 2;
    ll prod = 1;
    for (int i = 1; i <= n; i++) {
        prod = prod * i % a;
    }
    ll ans = gcd(prod, a);
    cout << ans << endl;
    return 0;
}

S 老师的签到

爆内存

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 2e6 + 10;
ll n, m;

string solve(int n, int m, vector<string> &M) {
    vector<vector<string>> dp(n, vector<string>(m, ""));

    // dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + M[i][j]
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            char s = M[i][j];
            if (i > 0 && j > 0) {
                if (dp[i - 1][j] < dp[i][j - 1]) {
                    dp[i][j] = dp[i - 1][j] + s;
                } else {
                    dp[i][j] = dp[i][j - 1] + s;
                }
            } else if (i > 0) {
                dp[i][j] = dp[i - 1][j] + s;
            } else if (j > 0) {
                dp[i][j] = dp[i][j - 1] + s;
            } else {
                dp[i][j] = s;
            }
        }
    }
    return dp[n - 1][m - 1];
}

int main() {
    cin >> n >> m;
    vector<string> M(n);
    for (int i = 0; i < n; i++) {
        cin >> M[i];
    }
    cout << solve(n, m, M) << endl;
    return 0;
}

优化,注意到dp**[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + M[i][j], 只遇到上一个和前一个状态,可以优化成一维。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 2e6 + 10;
ll n, m;

string solve(int n, int m, vector<string> &M) {
    vector<string> dp(m);
    dp[0] = M[0][0];
    for (int i = 1; i < m; i++) {
        dp[i] = dp[i - 1] + M[0][i];
    }
    for (int i = 1; i < n; i++) {
        dp[0] += M[i][0];
        for (int j = 1; j < m; j++) {
            dp[j] = min(dp[j], dp[j - 1]) + M[i][j];
        }
    }
    return dp[m - 1];
}

int main() {
    cin >> n >> m;
    vector<string> M(n);
    for (int i = 0; i < n; i++) {
        cin >> M[i];
    }
    cout << solve(n, m, M) << endl;
    return 0;
}

S 老师的求和

L1(k)=ak+bL2(k)=i=1kL1(i)L3(k)=i=1kL2(i)L4(k)=i=1kL3(i)\begin{aligned} & L_1(k)=a k+b \\ & L_2(k)=\sum_{i=1}^k L_1(i) \\ & L_3(k)=\sum_{i=1}^k L_2(i) \\ & L_4(k)=\sum_{i=1}^k L_3(i) \end{aligned}

由于答案可能很大,你只需要输出对 998244353998244353 取模的结果

推公式

#include <bits/stdc++.h>

using namespace std;
const int MOD = 998244353;
typedef long long ll;

ll a, b, x, t;
// 快速幂算法,计算 base^exponent % MOD
ll fastPow(ll base, ll exponent, ll MOD) {
    ll result = 1;
    base %= MOD;
    while (exponent > 0) {
        if (exponent & 1) result = (result * base) % MOD;
        base = (base * base) % MOD;
        exponent >>= 1;
    }
    return result;
}

// 计算逆元,这里 a 是要求逆元的数
ll modInverse(ll a, ll MOD) {
    return fastPow(a, MOD - 2, MOD); // 费马小定理
}

ll f1(ll n) {
    return (a * n + b) % MOD;
}

// 等差数列求和
ll f2(ll n) {
    return (n * b + a * (n + 1) % MOD * n % MOD * modInverse(2, MOD) % MOD) % MOD;
}

// n(n+1)(a(2n+1)+3a+6b) / 12
ll f3(ll n) {
    return n % MOD * (n + 1) % MOD * (a % MOD * (2 * n % MOD + 1) % MOD + 3 * a % MOD + 6 * b % MOD) % MOD *
           modInverse(12, MOD) % MOD;
}

ll f4(ll n) {
    // x(x+1)(3ax(x+1)+4a(2x+1)+9a+18b) / 6
    ll res = 0;
//    res += a * n * n * (n + 1) * (n + 1) / 2;
    res += a % MOD * n % MOD * n % MOD * (n + 1) % MOD * (n + 1) % MOD * modInverse(2, MOD) % MOD;
//    res += (6 * a + 6 * b) * n * (n + 1) * (2 * n + 1) / 6;
    res += (6 * a % MOD + 6 * b % MOD) * n % MOD * (n + 1) % MOD * (2 * n + 1) % MOD * modInverse(6, MOD) % MOD;
//    res += (4 * a + 6 * b) * n * (n + 1) / 2;
    res += (4 * a % MOD + 6 * b % MOD) * n % MOD * (n + 1) % MOD * modInverse(2, MOD) % MOD;
    res = res * modInverse(12, MOD) % MOD;
    return res;
}

int main() {
    cin >> t;
    cin >> a >> b >> x;
    cout << f1(x) << ' ' << f2(x) << ' ' << f3(x) << ' ' << f4(x) << endl;
}

其他解法

img

#include <bits/stdc++.h>

using namespace std;

const int N = 1e8 + 10, mod = 998244353;
// inv is the inverse of 1, 2, 3, 4
int inv[] = {1, 499122177, 166374059, 291154603};
int a, b;

long long L(int i, int k) {
    // i == 0
    // ans = inv[0] * (a * k + b)
    // i == 1
    
    int ans = 1ll * inv[i] * ((1ll * a * k + 1ll * i * a + 1ll * (i + 1) * b) % mod) % mod;
    for (int j = 0; j < i; ++j)
        ans = 1ll * ans * (k + j) % mod;
    return ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--) {
        int k;
        cin >> a >> b >> k;
        cout << L(0, k) << " " << L(1, k) << " " << L(2, k) << " " << L(3, k) << '\n';
    }
}