AtCoder Regular Contest 176 A~D

A.01 Matrix Again(思维)

题意:

有一个 N×NN \times NN×N 网格。让 (i,j)(i, j)(i,j) 表示从上往下第 iii 行,从左往上第 jjj 列的单元格。

您需要在每个单元格中填入 000111 。请构建一个满足以下所有条件的填充网格的方法:

  • 单元格 (A1,B1),(A2,B2)…(AM,BM)(A_1,B_1),(A_2,B_2)\dots (A_M,B_M)(A1,B1),(A2,B2)(AM,BM) 中包含 111
  • iii 行中的整数总和为 MMM(1≤i≤N)(1 \le i \le N)(1iN)
  • iii 列中的整数总和为 MMM(1≤i≤N)(1 \le i \le N)(1iN)

可以证明,在这个问题的约束条件下,至少有一种填充网格的方法可以满足条件。

分析:

SkS_kSk 是单元格 (i,j)(i,j)(i,j) 的集合,满足 i+j=k mod Ni + j = k \bmod Ni+j=kmodN 。现在,如果我们从 SkS_kSk 中选择 MMM 个不同的单元格,并在这些单元格中写入 111 ,则行和与列和将为 MMM
那么接下来就是为每个 iii 加入 SAi+Bi mod NS_{A_i + B_i \bmod N}SAi+BimodN ,需要注意如果有重复的Ai+Bi mod NA_i + B_i \bmod NAi+BimodN 中,则需要相应添加 SkS_kSk

代码:

#include <bits/stdc++.h>

using namespace std;
int n, m;

int main() {
    int T;
    T = 1;
    while (T--) {
        cin >> n >> m;
        vector<bool> vis(n);
        for (int i = 1; i <= m; i++) {
            int x, y;
            cin >> x >> y;
            x--, y--;
            vis[(x + y) % n] = 1;
        }
        vector<int> ans;
        for (int i = 0; i < n; i++)
            if (vis[i])
                ans.push_back(i);
        for (int i = 0; i < n; i++)
            if (!vis[i] && ans.size() < m)
                ans.push_back(i);
        cout << n * m << endl;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                int x = i, y = (ans[j] - i + n) % n;
                cout << x + 1 << " " << y + 1 << endl;
            }
        }
    }
    return 0;
}

B.Simple Math 4(数学)

题意:

2N2^N2N 除以 2M−2K2^M - 2^K2M2K 的余数的最后一位数字。

分析:

如果是 N≥MN \ge MNM ,我们可以变换 2N≡2N−2N−M(2M−2K)≡2N−(M−K)( mod  2M−2K)2^N \equiv 2^N - 2^{N-M}(2^M - 2^K) \equiv 2^{N-(M-K)} (\bmod\ 2^M - 2^K)2N2N2NM(2M2K)2N(MK)(mod 2M2K) ,这样就可以把 NNN 换成 N−(M−K)N-(M-K)N(MK) 。重复这一操作,我们就可以将其简化为 N<MN < MN<M 的情况。
那么,如果是 N,K=M−1N,K = M-1N,K=M1 ,我们就有 2N=2M−2K2^N = 2^M - 2^K2N=2M2K ,所以答案是 000 。否则,有 2N<2M−2K2^N < 2^M - 2^K2N<2M2K ,所以答案是 2N mod 102^N \bmod 102Nmod10

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int mod = 998244353;

LL binpow(LL a, LL b, LL m) {
    a %= m;
    LL res = 1;
    while (b > 0) {
        if (b & 1)
            res = res * a % m;
        a = a * a % m;
        b >>= 1;
    }
    return res;
}

LL n, m;

int main() {
    int T;
    cin >> T;
    while (T--) {
        LL k;
        cin >> n >> m >> k;
        n -= max(0ll, n - k) / (m - k) * (m - k);
        if (n == m - 1 && k == m - 1)
            cout << "0" << endl;
        else
            cout << binpow(2, n, 10) << endl;
    }
    return 0;
}

C.Max Permutation(图论)

题意:

输出满足以下条件的 (1,2,…,N)(1,2,\dots,N)(1,2,,N) 的排列 P=(P1,P2,…,PN)P=(P_1,P_2,\dots,P_N)P=(P1,P2,,PN) 的数量,并将答案对 998244353998244353998244353 取模。

  • max⁡(PAi,PBi)=Ci (1≤i≤M)\max(P_{A_i},P_{B_i}) = C_i\ (1 \le i \le M)max(PAi,PBi)=Ci (1iM) .

分析:

我们构建一个有 NNN 个顶点的图 GGG ,其中每个 iii 顶点 AiA_iAiBiB_iBi 之间都有一条权重为 CiC_iCi 的边。

如果有 max⁡(PAi,PBi)=Ci\max(P_{A_i},P_{B_i}) = C_imax(PAi,PBi)=Ci ,那么必须有 PAi,PBi≤CiP_{A_i},P_{B_i} \le C_iPAi,PBiCi 。据此,对于每一个 iii 我们都可以推导出一个形式为 Pi≤XiP_i \le X_iPiXi 的条件。(如果顶点 iiiGGG 中的一个孤立点,那么 Xi=∞X_i = \inftyXi= )。

假设Pi=kP_i = kPi=k 并且我们按照 k=N,N−1,...,1k = N,N-1,...,1k=N,N1,...,1的顺序依次处理 。在此过程中,我们进行以下情况的区分。

  • 如果有两个或两个以上的 iii 满足 Ci=kC_i = kCi=k。那么所有权重为 kkk 的边都必须有一个顶点 vvv 作为端点。这里需要注意的是,如果存在这样一个顶点 vvv ,那么它就是唯一确定的。如果 vvv 不存在或 Xv<kX_v < kXv<k 不存在,那么答案就是 000 。否则,我们设为 Pv=kP_v = kPv=k
  • 如果只有一个 iii 满足 Ci=kC_i = kCi=k。则在该边的端点中选择一个 Xj≥kX_j \ge kXjk 并设置 Pj=kP_j = kPj=k 。如果两个端点都满足条件,那么无论我们选择哪一个,情况都是一样的,因此我们将答案乘以 222 并设置 Pj=kP_j = kPj=k 。如果两个端点都不符合条件,答案就是000
  • 如果没有iii满足 Ci=kC_i = kCi=k。我们从 Xj≥kX_j \ge kXjkPjP_jPj 尚未确定的顶点中选择一个顶点,并设置为 Pj=kP_j = kPj=k 。如果不存在这样的顶点,答案就是 000 。如果存在多个这样的顶点,那么无论我们选择哪个顶点,情况都是一样的,我们将答案乘以候选顶点的数量,并为某个顶点设置 Pj=kP_j = kPj=k

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int MAXN = 3e5 + 10;
const int MOD = 998244353;
int n, m;
int tmp[MAXN];
int a[MAXN];
int u, v, w;
vector<pair<int, int> > e[MAXN];
LL ans = 1, res;

int main() {
    int T;
    T = 1;
    while (T--) {
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
            tmp[i] = n;
        }
        for (int i = 0; i < m; i++) {
            cin >> u >> v >> w;
            e[w].push_back(make_pair(u, v));
            tmp[u] = min(tmp[u], w);
            tmp[v] = min(tmp[v], w);
        }
        for (int i = 1; i <= n; i++) {
            a[tmp[i]]++;
        }
        for (int i = n; i >= 1; i--) {
            res += a[i];
            if (e[i].size() >= 2) {
                int tmp1 = -1;
                if (e[i][0].first == e[i][1].first || e[i][0].first == e[i][1].second) {
                    tmp1 = e[i][0].first;
                } else if (e[i][0].second == e[i][1].first || e[i][0].second == e[i][1].second) {
                    tmp1 = e[i][0].second;
                } else {
                    ans = 0;
                }
                for (int j = 2; j < e[i].size(); j++) {
                    if (e[i][j].first != tmp1 && e[i][j].second != tmp1) {
                        ans = 0;
                    }
                }
                if (tmp[tmp1] < i) {
                    ans = 0;
                }
                res--;
            } else if (e[i].size() == 1) {
                int num = 0;
                if (tmp[e[i][0].first] >= i) {
                    num++;
                }
                if (tmp[e[i][0].second] >= i) {
                    num++;
                }
                ans = (ans * num) % MOD;
                if (num) {
                    res--;
                }
            } else {
                ans = (ans * res) % MOD;
                if (res) {
                    res--;
                }
            }
        }
        cout << ans << endl;
    }
    return 0;
}

D.Swap Permutation (数学)

题意:

给你一个 (1,2,…,N)(1,2,\dots,N)(1,2,,N) 的排列组合 P=(P1,P2,…,PN)P=(P_1,P_2,\dots,P_N)P=(P1,P2,,PN) 。你将执行以下操作 MMM 次:

  • 选择一对整数 (i,j)(i, j)(i,j) ,使得 1≤i<j≤N1 \le i < j \le N1i<jN ,然后交换 PiP_iPiPjP_jPj

(N(N−1)2)M\left(\frac{N(N-1)}{2}\right)^M(2N(N1))M 个可能的操作序列。对于其中的每一个,考虑所有运算后的值 ∑i=1N−1∣Pi−Pi+1∣\sum\limits_{i=1}^{N-1} |P_i - P_{i+1}|i=1N1PiPi+1 。求所有这些值的和,并对998244353998244353998244353取模 。

分析:

将问题转化成:把PPP 中小于或等于 kkk 的值替换为 000 ,将大于 kkk 的值替换为 111 ,再计算 ∑i=1N−1∣Pi−Pi+1∣\sum\limits_{i=1}^{N-1} |P_i - P_{i+1}|i=1N1PiPi+1 的和,并将所有 k(1≤k≤N−1)k(1 \le k \le N-1)k(1kN1) 的和相加。

接下来解决 PPP 的所有元素都是 000111 的问题。考虑找出每个 iii 在操作后 ∣Pi−Pi+1∣=1|P_i - P_{i+1}| = 1PiPi+1=1 的序列数量。这可以通过对三个状态进行矩阵指数运算来实现,三个状态分别代表 PiP_iPiPi+1P_{i+1}Pi+1 中有 j(0≤j≤2)j(0 \le j \le 2)j(0j2) 个零。

对所有 i,ki,ki,k 进行上述运算的时间复杂度为 O(N2log⁡M)\mathrm{O}(N^2 \log M)O(N2logM) ,但如果使用累积和或类似方法,事先计算出每个 kkkiii 的个数,使得初始状态中 PiP_iPiPi+1P_{i+1}Pi+1 中不大于 kkk 的元素个数为 jjj ,就可以在 O(log⁡M)\mathrm{O}(\log M)O(logM) 中找到每个 kkk 的答案。

代码:

#include <bits/stdc++.h>

using namespace std;
const int maxn = 2e5 + 10;
const int mod = 998244353;

struct MAT {
    int z[3][3];

    MAT() {
        memset(z, 0, sizeof(z));
    }

    MAT operator*(MAT y) {
        MAT x = *this, ans;
        for (int i = 0; i < 3; ++i) {
            for (int k = 0; k < 3; ++k) {
                for (int j = 0; j < 3; ++j) {
                    ans.z[i][j] = (ans.z[i][j] + 1ll * x.z[i][k] * y.z[k][j]) % mod;
                }
            }
        }
        return ans;
    }

    MAT operator^(int y) {
        MAT x = *this, ans;
        for (int i = 0; i < 3; ++i) {
            ans.z[i][i] = 1;
        }
        while (y) {
            if (y & 1) {
                ans = ans * x;
            }
            x = x * x;
            y >>= 1;
        }
        return ans;
    }
};

int p[maxn], v[maxn], c[4];

int main() {
    int T;
    T = 1;
    while (T--) {
        int n, m;
        cin >> n >> m;
        for (int i = 1, x; i <= n; ++i) {
            cin >> x;
            p[x] = i;
        }
        int ans = 0;
        c[0] = n - 1;
        for (int i = 1; i < n; ++i) {
            int t = p[i];
            if (t != 1) {
                --c[(v[t - 1] << 1) | v[t]];
            }
            if (t != n) {
                --c[(v[t] << 1) | v[t + 1]];
            }
            v[t] = 1;
            if (t != 1) {
                ++c[(v[t - 1] << 1) | v[t]];
            }
            if (t != n) {
                ++c[(v[t] << 1) | v[t + 1]];
            }
            MAT tmp;
            tmp.z[0][0] = (1ll * n * (n - 1) / 2 - 2 * i) % mod;
            tmp.z[0][1] = 2 * i;
            tmp.z[1][0] = n - i - 1;
            tmp.z[1][1] = (1ll * n * (n - 1) / 2 - (n - i - 1) - (i - 1)) % mod;
            tmp.z[1][2] = i - 1;
            tmp.z[2][1] = (n - i) * 2;
            tmp.z[2][2] = (1ll * n * (n - 1) / 2 - (n - i) * 2) % mod;
            MAT tn = tmp ^ m;
            ans = (ans + 1ll * tn.z[0][1] * c[0] % mod + 1ll * tn.z[1][1] * (c[1] + c[2]) % mod +
                   1ll * tn.z[2][1] * c[3] % mod) % mod;
        }
        cout << ans << endl;
    }
    return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值