• Educational Codeforces Round 86 A


    A. Road To Zero

    题意:

      有(x, y)两个数,你的目的是让(x = 0 and y = 0),你可以花费(a)元使(x)(y)加减(1),也可以花费(b)元使(x)(y)加减(1)。求最小代价?

    题解:

       (if x*yleq 0,ans = (abs(x) + abs(y)) * a)
       (else,ans = min(abs(x + y) * a, min(abs(x), abs(y)) * b + abs(x - y) * a))

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int main(){
        int t; cin >> t;
        while(t --){
            ll a, b, x, y;
            cin >> x >> y >> a >> b;
            ll ans;
            if((x <= 0 && y >= 0) || (x >= 0 && y <= 0)){ // 异号只有一种做法
                ans = (abs(x) + abs(y)) * a;
            }
            else{ // 同号就两种情况取小值
                ll ans1, ans2;
                ans1 = (abs(x) + abs(y)) * a;
                ans2 = min(abs(x), abs(y)) * b + abs(abs(y) - abs(x)) * a;
                ans = min(ans1, ans2);
            }
            cout << ans << endl;
        }
        return 0;
    }
    

    B. Binary Period

    题意

      给你一个字符串(t),构造字符串(s),使得(t)(s)个子序列(不连续),并且(s)的循环周期(k)最小。
      例:(s = 0010010010),则(k = 3)。要求(len(s) leq 2 * len(t))

    题解

      根据他的长度要求,显然(k leq 2),所以我们只需要判断(t)是否是全(1)或全(0),如果是,直接输出(t),否则我们构造一个长度为(2 * len(t))的字符串(s),就可以符合要求

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int main(){
        int t; cin >> t;
        while(t --){
            string t, s = "";
            cin >> t;
            int mark = 0;
            for(int i = 1; i < t.size(); i ++) // 判断t是否包含0和1
                if(t[i] != t[i - 1]){
                    mark = 1;
                    break;
                }
            if(!mark){
                cout << t << endl;
            }else{
                for(int i = 0; i < t.size(); i ++){
                    s += '0'; s += '1';
                }
                cout << s << endl;
            }
        }
        return 0;
    }
    

    C. Yet Another Counting Problem

    题意:

      在区间([l, r])中有多少个x满足(((x mod a) mod b)≠((x mod b) mod a))

    题解:

      数论上来先打表!打表出奇迹!

      多枚举几个(a, b, l, r),把(x)打出来看,就可以发现其中的规律

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 210;
    
    int f[N * N];
    
    
    // 打表
    // void makeChart(int a, int b, int l, int r){
    //     for(int i = l; i <= r; i ++){
    //         if((i % a) % b != (i % b) % a){
    //             cout << i << endl;
    //         }
    //     }
    // }
    
    ll cal(ll n, ll x){
        return n / x * f[x] + f[n % x];
    }
    
    int main(){
        // freopen("output.txt", "w", stdout);
        // makeChart(4, 6, 1, 100);
        int t; cin >> t;
        while(t --){
            ll a, b, l, r, q;
            cin >> a >> b >> q;
            memset(f, 0, sizeof f);
            for(int i = 1; i <= a * b; i ++){
                f[i] = f[i - 1] + (i % a % b != i % b % a);
            }
            while(q --){
                cin >> l >> r;
                cout << cal(r, a * b) - cal(l - 1, a * b) << " ";
            }
            puts("");
        }
        return 0;
    }
    

    D. Multiple Testcases

    题意:

      给你(n)个数(m[i] leq k(i leq n)),再给你(k)个数(c[i](i leq k)),将这(n)个数分组,要求每组(geq i)的数的个数(leq c[i]),问最少可以分几组?

    题解:

      先求出最小的分组数,预处理出(geq i)的数的个数(cnt[i]),那么分组数(ans = max(ans, left lceil frac{cnt[i]}{c[i]} ight ceil)),然后将(m)排序,依次插入这(ans)个组,则第(i)组里面的数为:({m[i], m[i + ans], m[i + 2 * ans] ...})
      证明:以任意一组举例,其中(geq i)的数的个数(t = left lceil frac{cnt[i]}{ans} ight ceil),又因为(ans geq left lceil frac{cnt[i]}{c[i]} ight ceil),那么显然(t leq c[i]),故符合题意。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5 + 10;
    
    int n, k;
    int m[N], c[N], cnt[N];
    vector<int> vec[N];
    
    int main(){
        cin >> n >> k;
        for(int i = 1; i <= n; i ++){
            scanf("%d", &m[i]);
            cnt[m[i]] ++; // 记每个数出现的次数
        }
        for(int i = 1; i <= k; i ++){
            scanf("%d", &c[i]);
        }
        for(int i = k; i >= 1; i --){
            cnt[i] += cnt[i + 1]; // 算出>=i的个数
        }
        int ans = 1;
        for(int i = 1; i <= k; i ++){ // 计算组数
            int tmp = cnt[i] / c[i] + (cnt[i] % c[i] != 0);
            ans = max(ans, tmp);
        }
    
        sort(m + 1, m + n + 1);
        for(int i = 1; i <= n; i ++){ // 轮流插入
            vec[i % ans].push_back(m[i]);
        }
    
        cout << ans << endl;
        for(int i = 0; i < ans; i ++){
            cout << vec[i].size();
            for(int j = 0; j < vec[i].size(); j ++){
                printf(" %d", vec[i][j]);
            }
            puts("");
        }
        return 0;
    }
    

    E. Placing Rooks

    题意:

      在(n imes n)的棋盘中,你可以放(n)枚棋子,棋子可以攻击同行和同列距离最近的棋子(参考象棋中的车),在你摆放完所有棋子后,要求有(k)对棋子可以互相攻击,并且对于没有摆放棋子的空格来说,他的同行或同列至少有一枚棋子。求摆放的方案数?

    题解:

      显然要么每行都有一个棋子,要么每列都有一个棋子。
      我们考虑每行都有一个棋子。那么攻击的棋子只能在列中产生了。假设一列中有(x)枚棋子,那么有(x - 1)对棋子可以相互攻击,也就是说(n)枚棋子最多有(n - 1)对棋子可以相互攻击,那么对于(k > n - 1)的,我们可以直接过滤掉。
      那么对于每行都有一个棋子的情况下,要符合条件我们应该摆多少列呢(例如我们摆(n)列显然不符合条件)?
      假设我们摆(t)列,那么每列摆了(c_1,c_2,c_3......c_t)个,则可以得到两个方程:

    [c_1+c_2+c_3+......+c_t = n ]

    [(c_1-1)+(c_2-1)+(c_3-1)+......+(c_t-1)=k ]

      相减即可得到:(t = n - k),即要符合条件我们必须摆(n - k)列。
      接下来就是组合数学的问题了,我们将(n)枚棋子放入(t)列,每一枚棋子都有(t)种方法,则共有(t^n)种情况。但是我们发现可能出现了不足(t)列的情况,所以我们需要减去这些情况,即那些只放了(1,2,3,...,t-1)列的情况,但是这样不好计算,所以我们需要换种计算思路。
      我们在(t)列中选出一个空列(没有摆放棋子的),然后减去(n)枚棋子在剩余(t - 1)列摆放的情况,但和(t)列一样,也会出现没有摆满的情况,所以我们就减多了(由于(t-1)列中也有空列,就会再次被选出来,所以重复计算了),那么我们就需要加上多减的,再在(t-1)列中选出一个空列,加上(n)枚棋子在剩余(t-2)列中摆放的情况。但显然是存在摆不满的情况的,所以我们又加多了,又需要减,如此重复下去,直到最后只剩(1)列的情况。那么我们可以得到公式:

    [ans = t^n-C_{t}^{1} imes (t-1)^n + C_{t}^2 imes (t-2)^n - C_{t}^3 imes (t-3) ^ n + ... ]

      化简一下即:

    [ans = sum_{i=0}^{t}(-1)^i imes C_t^i imes (t-i)^n ]

      由于还要从(n)列里面选出(t)列,所以还要乘以(C_n^t)。最后我们将行列颠倒考虑,即还有相同的方案数,所以(ans = ans imes 2),但是当(k = 0)的时候,即没有两枚棋子可以相互攻击,即每行每列只有一枚棋子,这种情况下行列的情况都是一样,所有不用( imes 2)

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 200010, mod = 998244353;
    typedef long long ll;
    
    int n, k;
    ll fact[N]; // 阶乘
    
    ll fastPow(ll a, ll b){ // 快速幂
        ll res = 1;
        while(b){
            if(b & 1) res = res * a % mod;
            b >>= 1;
            a = a * a % mod;
        }
        return res;
    }
    
    ll cal(int a, int b){ // 计算组合数
         return fastPow(fact[b] * fact[a - b] % mod, mod - 2) * fact[a] % mod;
    }
    
    int main(){
        // freopen("input.txt", "r", stdin);
        cin >> n >> k;
        if(k >= n) { puts("0"); return 0; }
        fact[0] = 1;
        for(int i = 1; i <= n; i ++){
            fact[i] = fact[i - 1] * i % mod;
        }
        int c = n - k;
        ll ans = 0;
        for(int i = 0; i <= c; i ++){ // 求和
            ans = ((ans + cal(c, i) * fastPow(-1, i) * fastPow(c - i, n)) % mod + mod) % mod;
        }
    
        ans = ans * cal(n, c) % mod; // 乘上从n列里选(n - k)列的方案数
    
        if(k){
            ans = (ans << 1) % mod;
        }
    
        cout << ans << endl;
    
        return 0;
    }
    
  • 相关阅读:
    ECDSA—模乘模块
    ECDSA—模加减模块
    复微杯参赛感悟与总结
    利用system generator 生成vivado ip—以低通滤波器举例
    科普—为什么要用ECDSA加签及其数学上的验签证明
    查看CentOS版本号
    查看MySQL的版本号
    MySQL修改端口号
    CentOS7上安装MySQL
    CentOS7中装MySQL & yum install mysql-community-server问题
  • 原文地址:https://www.cnblogs.com/nonameless/p/12839082.html
Copyright © 2020-2023  润新知