1. 程式人生 > 資訊 >18 點加場多抽 1 次:天貓拼手氣抽 50 萬個 3 元起紅包

18 點加場多抽 1 次:天貓拼手氣抽 50 萬個 3 元起紅包

補題

A - Round decimals

將給定的浮點數四捨五入。

\(round(x)\)

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    double n;
    cin >> n;
    cout << round(n) << "\n";
    return 0;
}

B - Counting Arrays

\(n\) 個序列,求不同的序列個數?

\(set\) 去重

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
 
int n;
set<vector<int> > S;
 
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    while(n -- )
    {
        int k, x;
        vector<int> v;
        cin >> k;
        while(k -- )
        {
            cin >> x;
            v.push_back(x);
        }
        S.insert(v);
    }
    cout << (int)S.size() << "\n";
    return 0;
}

C - Martial artist

\(n\) 個動作需要學,學習動作 \(i\) 需要時間 \(T_i\), 只有學完一個動作才能學另一個,動作之間有依賴關係,求學會動作 \(n\) 所需的最少時間?

建圖,能夠走到動作 \(n\) 的所有動作都需要學,不妨把 \(n\) 當作起點,遍歷所有能到達的點。

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
 
typedef long long LL;
const int N = 2e5 + 20;
 
int n, w[N];
vector<int> G[N];
bool st[N];
LL ans;
 
void DFS(int u)
{
    st[u] = true;
    ans += w[u];
    for(auto v : G[u])
        if(!st[v])
            DFS(v);
}
 
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; ++ i)
    {
        cin >> w[i];
        int k, x; 
        cin >> k;
        while(k -- )
        {
            cin >> x;
            G[i].push_back(x);
        }
    }
    DFS(n);
    cout << ans << "\n";
    return 0;
}

D - Teleportation

二維座標系上有 \(n\) 個點,可以使用一種操作 \((a, b)\) , 從 \((x, y)\) 移動到 \((x + a, y + b)\)

若想在任意兩個點之間互相到達,所需的最少操作種類數?

\(g = gcd(a, b)\), \((a, b)\) 一定可以由 \((\dfrac{a}{g}, \dfrac{b}{g})\) 湊出。

統計任意兩點 \(i\)\(j\) 間距離 \((x_i - x_j, y_i - y_j)\), 令 \(g_{i, j} = gcd(|x_i - x_j|, |y_i - y_j|)\)

答案即為 \((\dfrac{x_i - x_j}{g_{i,j}}, \dfrac{y_i - y_j}{g_{i,j}})\) 的不同種類數。

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
 
const int N = 500 + 10;
 
int n;
int x[N], y[N];
set<pair<int, int> > S;
 
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);    
    cin >> n;
    for(int i = 1; i <= n; ++ i)
        cin >> x[i] >> y[i];
    for(int i = 1; i <= n; ++ i)
        for(int j = 1; j <= n; ++ j)
        {
            if(i == j) continue;
            int xx = x[i] - x[j], yy = y[i] - y[j];
            int g = __gcd(xx, yy);
            if(g < 0) g = -g; 
            xx /= g, yy /= g;
            S.insert({xx, yy});
        }
    cout << S.size() << "\n";
    return 0;
}

E - Just one

給定 \(n\) 個點 \(m\) 條邊的簡單無向圖,每一條邊可以給定一個方向,共有 \(2^m\) 中方案,統計每個點有且只有一條出邊的方案數。

若某個連通分支只有單個環,貢獻為 \(2\) , 其他情況均不合法,

問題轉化為求只有單個環的聯通分支個數 \(t\) , 答案即為 \(2^t\)

如何判斷一個連通分支是否是單個環: 若無環,則 \(n\) 個點的分支有 \(n - 1\) 條邊;若有一個環,則邊數 \(+1\),有 \(n\) 條邊;若有多個環,邊數 \(\ge n\) ; 故只需要判斷該聯通分支點數和邊數是否相等即可

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
 
const int N = 2e5 + 20;
const int P = 998244353;
 
int n, m;
vector<int> G[N];
bool st[N];
int point, edge;
 
void DFS(int u)
{
    point ++;
    edge += G[u].size(); 
    st[u] = true;
    for(auto v : G[u])
        if(!st[v])
            DFS(v);
}
 
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= m; ++ i)
    {
        int a, b; 
        cin >> a >> b;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    int ans = 1;
    for(int i = 1; i <= n; ++ i)
        if(!st[i])
        {
            point = edge = 0;
            DFS(i);
            if(point * 2 == edge) ans = 2ll * ans % P;
            else ans = 0;
        }
    cout << ans << "\n";
    return 0;
}

F - Score of Permutations

對於一個給定的排列 \(P = (p_1, p_2, ..., p_n)\), 定義 \(S(P)\) 為:

初始時刻,每個人手中有一個球,每經過一輪,第 \(i\) 個人把手上的球傳給 \(p_i\), \(S(P)\) 為至少經過一輪,每個人初始的球重新回到各自手上的最少輪數。

\(S_n\)\((1, 2, ..., N)\) 的所有排列,求\(\sum\limits_{P\in S_n}S(P)^k \; mod\; 998244353\)

首先考慮對於一個排列 \(P\) , 如何計算它的 \(S(P)\) , 從 \(i\)\(p_i\) 連一條邊,可以發現球會在每一個環之間迴圈傳遞,假設形成了 \(k\) 個環,環的大小分別為 \((c_1,c_2,..., c_k)\), 當所有環都回到初始狀態時, 所需的輪數為 \(lcm(c_1, c_2, ..., c_k)\)

考慮 \(dp\) 計算答案, 令 \(dp[i][j]\) 為給 \(i\) 個人已經分配好在哪個環中,且當前所有環的 \(lcm\)\(j\) 的方案數,轉移方程為:

\[dp[i + x][lcm(j, x)] = dp[i][j] * C_{n- i - 1} ^ {x - 1} * (x - 1)! \]

對於上述狀態轉移方程,我們挑出 \(x\) 個人組成一個新的環,為了保證不會重複,可以給每次選出的環有序化,即每次先從剩下的人裡面選擇一個編號最小的,然後再從剩餘的 \(n - i - 1\) 個人中選出 \(x - 1\) 個組成這個大小為 \(x\) 的環,即 \(C_{n - i - 1}^{x - 1}\)。對於選出的 \(x\) 個數,排列共有 \(x!\) 種, 又因為是環,所以有 \((x - 1)!\) 種方案。

由於 \(lcm\) 過大,狀態轉移可以用 \(map\) 實現; 初始狀態為 \(dp[0][1] = 1\),答案即為 \(\sum dp[n][s] * s^k\)

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int P = 998244353;
const int N = 50 + 5;

LL n, k, fac[N], C[N][N];
map<LL, LL> dp[N];

LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; }
LL lcm(LL a, LL b) { return a * b / gcd(a, b); }
LL power(LL a, LL b)
{
    LL res = 1;
    for(; b; b >>= 1, a = a * a % P)
        if(b & 1) res = res * a % P;
    return res;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    fac[0] = 1;
    for(int i = 1; i < N; ++ i) fac[i] = fac[i - 1] * i % P;
    for(int i = 0; i < N; ++ i)
        for(int j = 0; j <= i; ++ j)
        {
            if(!j) C[i][j] = 1;
            else C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
        } 
    cin >> n >> k;
    dp[0][1] = 1;
    for(int i = 0; i < n; ++ i)
        for(auto [p, q] : dp[i])
            for(int x = 1; x <= n - i; ++ x)
            {
                LL& d = dp[i + x][lcm(p, x)];
                LL t = dp[i][p] * C[n - i - 1][x - 1] % P * fac[x - 1] % P;
                d = (d + t) % P; 
            }
    LL ans = 0;
    for(auto [p, q] : dp[n])
        ans = (ans + power(p, k) * q % P) % P;
    cout << ans << "\n";
    return 0;    
}