Loading

AtCoder Beginner Contest 262 A - F 个人题解

传送门

A - World Cup

判断一下模

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
using namespace std;

int main()
{
    int x;
    cin >> x;
    x -= 2;
    x += (4 - x % 4) % 4;
    x += 2;
    cout << x << endl;
    return 0;
}

B - Triangle (Easier)

枚举

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
using namespace std;
const int maxn = 110;
int gra[maxn][maxn];

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i=0; i<m; i++)
    {
        int a, b;
        cin >> a >> b;
        gra[a][b] = 1;
        gra[b][a] = 1;
    }
    int ans = 0;
    for(int i=1; i<=n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            for(int k=j+1; k<=n; k++)
            {
                if(gra[i][j] && gra[j][k] && gra[k][i])
                    ans++;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

C - Min Max Pair

枚举每个位置作为 \(a_i\) 所提供的贡献:

  1. \(a_i = i\):贡献为所有 \(a_j == j\)\(j > i\) 的情况

  2. \(a_i > i\):如果 \(a_{a_i} = i\) 则贡献增加一

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 10;
int num[maxn], alp[maxn];

int main()
{
    int n;
    cin >> n;
    for(int i=1; i<=n; i++)
    {
        cin >> num[i];
        if(num[i] == i) alp[i]++;
        alp[i] += alp[i-1];
    }
    ll ans = 0;
    for(int i=1; i<=n; i++)
    {
        if(num[i] == i) ans += alp[n] - alp[i];
        else if(num[i] > i && num[num[i]] == i) ans++;
    }
    cout << ans << endl;
    return 0;
}

D - I Hate Non-integer Number

dp

如果能平均分,说明选了 \(n\) 个数字,且其和在 \(n\) 的意义下为 \(0\)

因此考虑用 \(dp\)\(n\) 次,第 \(i\) 次代表选择 \(i\) 个数字

\(dp[j][k][u]\) 代表前 \(j\) 个数字,选择 \(k\) 个数字,模意义下为 \(u\) 的个数

因此有状态转移方程

\(dp[j][k][u] = dp[j-1][k][u] + dp[j-1][k-1][(u-a_j)\%i]\)

显然转移的话,对于第二维和第三维都要进行转移,因此转移复杂度为 \(O(n^2)\)

总的算法复杂度为 \(O(n^4)\)

我就是算出这个复杂度,然后不敢写,寄

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
using namespace std;
typedef long long ll;
const int maxn = 110;
const ll mod = 998244353;
ll dp[maxn][maxn][maxn];
ll a[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    for(int i=1; i<=n; i++) cin >> a[i];
    ll ans = 0;
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=n; j++)
            for(int k=0; k<=i; k++)
                for(int u=0; u<i; u++)
                    dp[j][k][u] = 0;
        for(int i=0; i<=n; i++) dp[i][0][0] = 1;
        for(int j=1; j<=n; j++)
        {
            for(int k=i; k; k--)
            {
                for(int u=0; u<i; u++)
                {
                    dp[j][k][u] = (dp[j-1][k][u] + dp[j-1][k-1][(u-a[j]%i+i)%i]) % mod;
                    int x = 0;
                    x += 1;
                }
            }
        }
        ans += dp[n][i][0];
    }
    cout << ans % mod << endl;
    return 0;
}

E - Red and Blue Graph

思维 + 组合数

从点上想,一直找不到突破口,考虑从边的角度开始想

如果一条边两个端点都是红点,则说明这个边消耗红点的度数为 \(2\)

如果一条边有一个端点是红点,则消耗红点度数为 \(1\)

我们可以从这里开始突破,假设红色点的总度数为 \(D\),两个端点都是红色的边数为 \(R\),两个端点不同色的边数为 \(G\)

则有:\(D = 2 * R + G\)

由于题目限定 \(G\) 一定是个偶数,因此 \(D\) 也必然是个偶数

因此只用考虑奇数度数的点,如何分配 \(K\) 个红色的点

每次考虑奇数的点都是偶数个

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const int maxn = 2e5 + 10;
vector<int>gra[maxn];
ll init[maxn];

ll qpow(ll x, ll n)
{
    ll ans = 1;
    while(n)
    {
        if(n & 1) ans = ans * x % mod;
        n >>= 1;
        x = x * x % mod;
    }
    return ans % mod;
}

ll calc(ll up, ll down)
{
    ll a = init[down];
    ll b = init[down - up] * init[up] % mod;
    return a * qpow(b, mod - 2) % mod;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, m, k;
    cin >> n >> m >> k;
    init[0] = 1;
    for(int i=1; i<=n; i++) init[i] = init[i-1] * i % mod;
    for(int i=0; i<m; i++)
    {
        int a, b;
        cin >> a >> b;
        gra[a].push_back(b);
        gra[b].push_back(a);
    }
    int odd = 0, even = 0;
    for(int i=1; i<=n; i++)
    {
        if(gra[i].size() & 1) odd++;
        else even++;
    }
    ll ans = 0;
    int s = max(0, k - even);
    if(s & 1) s++;
    for(int i=s; i<=min(odd, k); i+=2)
        ans = (ans + calc(i, odd) * calc(k - i, even)) % mod;
    cout << ans << endl;
    return 0;
}

F - Erase and Rotate

思维 + 单调栈

  1. 考虑只能删除的情况:

由于我们要求字典序最小,因此考虑在前 \(k + 1\) 个数字中,我们选取最小的,然后把在他前面的所有数字全部删掉,每删掉一个都让 \(k\) 减小 \(1\)

然后保留那个最小的数字,往后继续上述过程

如果跑到最后仍有 \(k > 0\),则考虑删掉后面 \(k\) 个数字

这个过程可以用单调栈 \(O(n)\) 实现,每次弹出一个数字则让 \(k-1\)

  1. 考虑能够翻转的情况

贪心地想,我们找到一个字典序最小的,并且能够让其翻转到最前面来的数字,然后把它本身以及它后面的全部数字翻转到前面来,再进行删除操作

因为翻转本身就消耗了一点,因此删除被翻转到前面的数字时,不需要消耗能量

但是上述情况并不是最优,有可能无需翻转,直接删除就可以找到最优,因此要考虑这两种情况最后的出来的最小字典序作为答案

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 2e5 + 10;
int num[maxn], alp[maxn];
int n;

vector<int> solve(int k, int r = -1)
{
    vector<int>q;
    for(int i=1; i<=n; i++)
    {
        while(q.size() && num[q.back()] > num[i] && (k || q.back() <= r))
        {
            if(q.back() > r) k--;
            q.pop_back();
        }
        q.push_back(i);
    }
    for(int i=0; i<k; i++)
    {
        if(alp[q.back()] <= r) k++;
        q.pop_back();
    }
    for(int i=0; i<q.size(); i++) q[i] = num[q[i]];
    return q;
}

void print(const vector<int> &x)
{
    int cur = 0;
    for(int i=0; i<x.size(); i++)
    {
        if(cur++) cout << " ";
        cout << x[i];
    }
    cout << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int k;
    cin >> n >> k;
    for(int i=1; i<=n; i++)
    {
        cin >> num[i];
        alp[num[i]] = i;
    }
    auto a = solve(k);
    int s = 0;
    for(int i=1; i<=n; i++)
    {
        if(n - alp[i] + 1 <= k)
        {
            s = n - alp[i] + 1;
            for(int j=1; j<=n; j++)
                alp[j] = (alp[j] - 1 + s) % n + 1;
            break;
        }
    }
    for(int i=1; i<=n; i++) num[alp[i]] = i;
    auto b = solve(k - s, s);
    auto ans = min(a, b);
    print(ans);
    return 0;
}
posted @ 2022-08-05 20:37  dgsvygd  阅读(49)  评论(0编辑  收藏  举报