2021年ACM竞赛班训练(三)

2021年ACM竞赛班训练(三)

A题 验证哥德巴赫猜想

请移步验证哥德巴赫猜想

B题 路径

请移步路径

C题 组合数

原题链接

思路

  1. 预处理组合数:
    \(m\)\(n\)的范围均在\(2000\)以内, 可以采用\(O(N^2)\)的算法预处理所有的组合数.
    求解组合数的几种算法详见组合数
  2. 预处理二维前缀和:
    由于多组测试数据, 仅仅预处理出组合数是不够的, 必须预处理所有的结果, 这样对于每次询问都达到\(O(1)\)
    运用动态规划的思想(也可以理解为二维前缀和):

状态表示:

\(dp[i][j]\): \(C^{0 - j}_{0 - i}\)中满足整除\(k\)的集合
属性: 数量

状态计算:

\(dp[i - 1][j]\): \(C^{0 - j}_{0 - (i - 1)}\)中满足整除\(k\)的集合
\(dp[i][j - 1]\): \(C^{0 - (j - 1)}_{0 - i}\)中满足整除\(k\)的集合
以上两部分有重合:
\(dp[i - 1][j - 1]\): \(C^{0 - (j - 1)}_{0 - (i - 1)}\)中满足整除\(k\)的集合
得到递推式:

\[dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] \]

代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 2010;
int t, k;
int c[N][N];
int res[N][N];

void init()
{
    for (int i = 0; i < N; i ++ ){
        for (int j = 0; j <= i; j ++ )
            if (j == 0)
                c[i][j] = 1;
            else{
                c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % k;
                res[i][j] = res[i - 1][j] + res[i][j - 1] - res[i - 1][j - 1];
                if (c[i][j] == 0)
                    res[i][j]++;
            }
        res[i][i + 1] = res[i][i];      //向下一层传递当前总数
    }
}

void solve()
{
    int n, m;
    scanf("%d%d", &n, &m);
    
    m = min(m, n);
    cout << res[n][m] << endl;

    return;
}

int main()
{
    cin >> t >> k;
    init();
    while (t -- )
        solve();
    return 0;
}

E题 a math problem(hard version)

原题链接

思路:

\(l, r\)范围均在\(1 - 1e18\), 暴力枚举肯定会超时.
应用容斥原理(原理详见:容斥原理)
对于每个数\(x\), 我们对其分解质因数, 分解得到\(p_{1}, p_{2}...p_{m}\), 共计\(m\)个质因数, 问题转化为:
\(l-r\)的所有数中, 有多少数能够至少被这\(m\)个质数中的一个整除.(详见容斥原理中的例题)

那么解决该问题只需两步即可:

  1. \(x\)分解质因数
  2. 分别对区间\([1, l- 1]\)\([1,r]\)应用容斥原理, 结果相减即可.

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
typedef long long LL;
// 容斥原理

long long s[100010];
int n = 0;

void divide(LL x)
{
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0){
            s[n ++] = i;
            while (x % i == 0)
                x = x / i;
        }
    if (x > 1)
        s[n ++] = x;
    return;
}

LL solve(LL m)
{
    LL ans = 0;
    for (int i = 1; i < 1 << n; i ++ ){
        LL t = 1;                   //当前二进制所表示的所有集合内的数字的质数之积
        int cnt = 0;                //当前二进制表示的集合个数


        for (int j = 0; j < n; j ++ ){        //枚举当前二进制的每一位
            if (i >> j & 1){
                if (t * s[j] > m){          //当前集合内的数
                    t = -1;
                    break;
                }
                cnt++;
                t = t * s[j];
            }
        }
        
        if (t != -1){
            if (cnt % 2)
                ans += m / t;
            else
                ans -= m / t;
        }

    }
    return m - ans;
}

int main()
{
    long long l, r, x;
    cin >> l >> r >> x;

    divide(x);  //分解质因数

    cout << solve(r) - solve(l - 1) << endl;
    return 0;
}

为什么这样做不会超时:

25以内的质数(9个):
2 3 5 7 11 13 17 19 23
它们的乘积:\(223092870>2e9\)
即对于任意的 \(x<2e9\) :不会超过\(9\)个不同的质因子
即:应用容斥原理时最多有\(9\)个集合,总共需要枚举的集合的组合方式有\(2^{9}\)
对于每种组合方式, 我们枚举二进制的每一位时, 最多有\(9\)
即时间复杂度大概在\(2 \times 9\times 2^{9}\)

F题: 矩阵变换

原题链接

思路

题目数据范围不大, 直接根据题意模拟即可

代码

#include <iostream>
#include <cstring>
#include <string>

using namespace std;

const int N = 510;
int n, m, k;
char s[N][N];
int a, b, c, d;
int op;
void turnup()
{
    if ((c + d) % 2 == 0){
        int t = (c + d) / 2;
        for (int i = a; i <= b; i ++ ){
            for (int j = c; j < t; j ++ ){
                char cs = s[i][j];
                s[i][j] = s[i][2 * t - j];
                s[i][2 * t - j] = cs;
            }
        }
    }else{
        int t = (c + d) / 2;
        for (int i = a; i <= b; i ++ )
            for (int j = c; j <= t; j ++ ){
                char cs = s[i][j];
                s[i][j] = s[i][2 * t + 1 - j];
                s[i][2 * t + 1 - j] = cs;
            }
    }
}

void turndown()
{
    if ((a + b) % 2 == 0){
        int t = (a + b) / 2;
        for (int i = c; i <= d; i ++ ){
            for (int j = a; j < t; j ++ ){
                char cs = s[j][i];
                s[j][i] = s[2 * t - j][i];
                s[2 * t - j][i] = cs;
            }
        }
    }else{
        int t = (a + b) / 2;
        for (int i = c; i <= d; i ++ )
            for (int j = a; j <= t; j ++ ){
                char cs = s[j][i];
                s[j][i] = s[2 * t + 1 - j][i];
                s[2 * t + 1 - j][i] = cs;
            }
    }
}

void solve()
{

    cin >> op >> a >> b >> c >> d;
    if (op == 1){
        turnup();
    }else{
        turndown();
    }
    for (int i = 1; i <= n; i ++ ){
        cout << s[i] + 1;
        puts("");
    }
    puts("");
    return;
}

int main()
{
    
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i ++ )
        cin >> s[i] + 1;
    while (k--)
        solve();
    return 0;
}
posted @ 2021-04-09 15:56  lhqwd  阅读(213)  评论(0)    收藏  举报