AtCoder Beginner Contest 228(E、F补题)

E - Integer Sequence Fair

题意: P = 998244353 P=998244353 P=998244353输入三个数, M , N , K M,N,K M,N,K 就是需要求 M k N m o d M^{k^N} mod MkNmod P P P
思路: 由于数据范围很大 所以需要先处理 K N K^N KN,再处理 M k N M^{k^N} MkN
根据费马小定理 如果 g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1那么 a p − 1 ≡ 1 a^{p-1}≡1 ap11( m o d p mod p modp)
在这个题中,由于 P P P是素数,所以除了 M M M P P P的倍数以外的情况, 一定会出现 g c d ( P , M ) = 1 gcd(P,M)=1 gcd(P,M)=1
所以分类讨论即可
1. M M M P P P的倍数,那么 M m o d P = 0 MmodP=0 MmodP=0所以输出0即可
q q q K N K^N KN除以 P − 1 P-1 P1的商, r r r K N K^N KN除以 P − 1 P-1 P1的余数
2. M M M不是 P P P的倍数
q q q K N K^N KN除以 P − 1 P-1 P1的商, r r r K N K^N KN除以 P − 1 P-1 P1的余数根据上面的结论, M K N M^{K^N} MKN = M q ( P − 1 ) + r = M q ( P − 1 ) × M r ≡ 1 q × M r ≡ M r ( m o d P ) =M^{q(P-1)+r}=M^{q(P-1)}×M^r≡1^q×M^r≡M^r(modP) =Mq(P1)+r=Mq(P1)×Mr1q×MrMr(modP)
所以就是先算 K N m o d ( P − 1 ) K^Nmod(P-1) KNmod(P1) t e m p temp temp,再算 M t e m p ( m o d P ) M^{temp}(modP) Mtemp(modP)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int MOD = 998244353;

int phi(ll x) {
    ll res = x;
    for (ll i = 2; i <= x / i; i++) {
        if (x % i == 0) {
            res = res / i * (i - 1);
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) res = res / x * (x - 1);
    return res;
}

int qmi(int a, int k, int mod) {
    int res = 1;
    a %= mod;
    while (k) {
        if (k & 1) res = res * a % mod;
        a = a * a % mod;
        k >>= 1;
    }
    return res;
}

signed main() {
    int n, k, m;
    cin >> n >> k >> m;
    ll p = phi(MOD);
    if (m % MOD == 0) {
        cout << 0 << endl;
        return 0;
    }
    int temp = qmi(k, n, p);
    int res = qmi(m, temp, MOD);
    cout << res << endl;
}

在快速幂中之所以要特判 M ≡ 0 ( m o d p ) M≡0(modp) M0(modp)的情况,是因为如果出现指数变成 0 0 0,那么写的快速幂由于没有特判就会直接输出 1 1 1,但是本应该输出 0 0 0
但是也可以不特判,因为只要保证指数 > 0 >0 >0即可,因为通过前提推论可知, M P − 1 ≡ 1 ( m o d P ) M^{P-1}≡1(modP) MP11(modP),所以可以直接在第一次的快速幂中加上 P − 1 P-1 P1
不会影响等式,因为 M K N × M P − 1 ≡ M K N ( m o d P ) M^{K^N}×M^{P-1}≡M^{K^N}(modP) MKN×MP1MKN(modP)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int MOD = 998244353;

int phi(ll x) {
    ll res = x;
    for (ll i = 2; i <= x / i; i++) {
        if (x % i == 0) {
            res = res / i * (i - 1);
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) res = res / x * (x - 1);
    return res;
}

int qmi(int a, int k, int mod) {
    int res = 1;
    a %= mod;
    while (k) {
        if (k & 1) res = res * a % mod;
        a = a * a % mod;
        k >>= 1;
    }
    return res;
}

signed main() {
    int n, k, m;
    cin >> n >> k >> m;
    ll p = phi(MOD);
    int temp = qmi(k, n, p) + p;
    int res = qmi(m, temp, MOD);
    cout << res << endl;
}

F - Stamp Game.

题意:
给定一个 N × M N×M N×M的矩阵 A i , j A_{i,j} Ai,j,高桥可以将 h 1 × w 1 h_1×w_1 h1×w1大小内的矩阵涂黑,即将这个区域内的格子涂满黑色,青木可以将 h 2 × w 2 h_2×w_2 h2×w2大小内的矩阵涂白,一开始矩阵内的方格都是白色的,最终高桥的分数就是矩阵内黑色方格的分数总和,高桥相让分数最大化,青木想让他的分数最小化,两人都是最聪明的,问双方涂完色后,高桥可能得到的最高分数是多少。
思路:
高桥的每个对应的矩阵的分数总和就是 s 1 ( i T , j T ) = ∑ i = i T i T + h 1 − 1 ∑ j = j T j T + w 2 − 1 A i , j s1(i_T,j_T)=\sum \limits_{i=i_T} ^{i_T+h_1-1}\sum \limits_{j=j_T} ^{j_T+w_2-1}A_{i,j} s1(iT,jT)=i=iTiT+h11j=jTjT+w21Ai,j
青木覆盖的面积分数和为 s 2 ( i T , j T ) = ∑ i = i T i T + h 1 − 1 ∑ j = j T j T + w 2 − 1 A i , j s2(i_T,j_T)=\sum \limits_{i=i_T} ^{i_T+h_1-1}\sum \limits_{j=j_T} ^{j_T+w_2-1}A_{i,j} s2(iT,jT)=i=iTiT+h11j=jTjT+w21Ai,j
最终答案就是 M ( i t , j t ) = s 1 ( i t , j t ) − m a x [ s 2 ( i , j ) ] ( i t ≤ i ≤ i t + h 1 − h 2 , j t ≤ j ≤ j t + w 1 − w 2 ) M(i_t,j_t)=s1(i_t,j_t)-max[s2(i,j)](i_t≤i≤i_t+h_1-h_2,j_t≤j≤j_t+w_1-w_2) M(it,jt)=s1(it,jt)max[s2(i,j)](itiit+h1h2,jtjjt+w1w2)
最终两重循环找 M ( i , j ) M(i,j) M(i,j)中的最大值
需要通过滑动窗口或者线段树或者ST表来加速寻找 m a x [ s 2 ( i , j ) ] max[s2(i,j)] max[s2(i,j)],从而算出 M ( i , j ) M(i,j) M(i,j)
下面是滑动窗口的做法,先固定行然后对列进行滑动窗口操作,求出窗口内的最大值,窗口大小就是 m 1 − m 2 + 1 m_1-m_2+1 m1m2+1,定义 f ( i , j ) f(i,j) f(i,j)为固定行后,算出的青木覆盖的最大分数,再固定列然后对行进行滑动窗口,窗口大小就是 h 1 − h 2 + 1 h_1-h_2+1 h1h2+1,定义 g ( i , j ) g(i,j) g(i,j)就是在矩阵右下角是 ( i , j ) (i,j) (i,j)大小为 h 1 × w 1 h_1×w_1 h1×w1的矩阵中,覆盖大小为 h 2 × w 2 h_2×w_2 h2×w2矩阵的最大值,算出 g ( i , j ) g(i,j) g(i,j),最后在循环跑最大值即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
using ll = long long;

ll a[N][N];
ll s[N][N];
ll s1[N][N];
ll s2[N][N];
ll f[N][N], g[N][N];
int n, m;
int n1, n2, m1, m2;
ll get(int x2, int y2, int x1, int y1) {
    return s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
}
int main() {
    cin >> n >> m >> n1 >> m1 >> n2 >> m2;
    n2 = min(n1, n2);
    m2 = min(m1, m2);

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
        }
    }
    //第一个人能删除的矩阵和
    for (int i = n1; i <= n; i++) {
        for (int j = m1; j <= m; j++) {
            s1[i][j] = get(i, j, i - n1 + 1, j - m1 + 1);
        }
    }
    //第二个人能覆盖的矩阵和
    for (int i = n2; i <= n; i++) {
        for (int j = m2; j <= m; j++) {
            s2[i][j] = get(i, j, i - n2 + 1, j - m2 + 1);
        }
    }

    //固定行,滑动窗口为列
    for (int i = 1; i <= n; i++) {
        multiset<ll> q;
        for (int j = 1; j <= m; j++) {
            q.insert(s2[i][j]);
            if (j > (m1 - m2 + 1)) {
                auto it = q.find(s2[i][j - (m1 - m2 + 1)]);
                q.erase(it);
            }
            f[i][j] = *q.rbegin();
        }
    }

    //固定列,滑动窗口为行
    for (int j = 1; j <= m; j++) {
        multiset<ll> q;
        for (int i = 1; i <= n; i++) {
            q.insert(f[i][j]);
            if (i > n1 - n2 + 1) {
                auto it = q.find(f[i - (n1 - n2 + 1)][j]);
                q.erase(it);
            }
            g[i][j] = *q.rbegin();
        }
    }

    ll res = 0;

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            res = max(res, s1[i][j] - g[i][j]);
        }
    }
    cout << res << endl;
}

To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值