2023杭电暑假多校10 题解 3 4 12 | JorbanS

文章目录

  • [3-Many Topological Problems](https://vjudge.csgrandeur.cn/problem/HDU-7385)
  • [4-Do You Like Interactive Problems?](https://vjudge.csgrandeur.cn/problem/HDU-7386)
  • [12-Equalize the Array]()

3-Many Topological Problems

题意 问能构造多少序列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n 每个对应 n n n 个节点的树,使得 a p i < a i ≤ a p i + k a_{p_i}api<aiapi+k,其中 p i p_i pi i i i 的父节点

题解 对于每一种序列,其构造树的方式都是独立的,即序列的序号和权值两者并无关系,即可分开计算,最后乘法原理。

对于序列的序号来说,没有任何限制,即可任意搭配,排列组合可以构成 n ! n! n! 种序列

对于序列的权值来说,有 a p i < a i ≤ a p i + k a_{p_i}api<aiapi+k,故按照点权从小到大插入点 i ( i > 1 ) i(i>1) i(i>1),有 m i n ( i − 1 , k ) min(i-1,k) min(i1,k) 种方案,则一共 n ! ∏ i = 2 n m i n ( i − 1 , k ) n!\prod_{i=2}^nmin(i-1,k) n!i=2nmin(i1,k) 种方案

  • n = k n=k n=k 时, r e s = n ! ( n − 1 ) ! res=n!(n-1)! res=n!(n1)!
  • n ≠ k n\neq k n=k 时, r e s = n ! k ! k n − k − 1 res=n!k!k^{n-k-1} res=n!k!knk1

对于阶乘可以先预处理

inline int qpow(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = (ll)res * a % mod;
        a = (ll)a * a % mod;
        b >>= 1;
    }
    return res;
}

int solve() {
    int n, k; cin >> n >> k;
    int res = 0;
    if (n == k) res = (ll)fac[n] * fac[n - 1] % mod;
    else res = (ll)fac[n] * fac[k] % mod * qpow(k, n - k - 1) % mod;
    return res;
}

int main() {
    fac[0] = 1;
    for (int i = 1; i < N; i ++) fac[i] = (ll)fac[i - 1] * i % mod;
    FastIO
    Cases
    cout << solve() << endl;
    return 0;
}

4-Do You Like Interactive Problems?

题意 有一个未知数 x , x ∈ [ 1 , n ] x,x\in[1,n] x,x[1,n],每次随机询问一个数 y , y ∈ [ 1 , n ] y,y\in[1,n] y,y[1,n],会被告知 y y y x x x 的大小关系( < , > , = <,>,= <,>,=),问知道 x x x 的询问次数的期望值

题解 可以打表,大概确定

// 打表 code
double solve(int n, int x) {
    srand((int)time(0));
    ll res = 0;
    for (int i = 0; i < N; i ++) {
        set<int> s;
        int cnt = 0;
        while (true) {
            int y = rand() % n + 1;
            s.insert(y);
            if (s.count(x)) break;
            if (x == 1) {
                if (s.count(2)) break;
            } else if (x == n) {
                if (s.count(n - 1)) break;
            } else {
                if (s.count(x - 1) && s.count(x + 1)) break;
            }
            cnt ++;
        }
        res += cnt;
    }
    return (double)res / N;
}

打表结果

1 2 3 4 5 6 17 18 48 49 50 51 52 53 54 55 56 57 58 59 60
50 23.042 32.135 32.063 33.659 31.525 31.217 32.559 33.496 32.194 33.28 23.592
51 24.631 33.873 33.5 31.252 31.252 31.494 33.697 33.776 34.81 32.896 31.802 25.459
56 27.261 37.371 36.889 37.047 37.343 38.375 36.585 35.798 37.205 36.463 36.333 35.488 36.48 37.561 36.833 34.481 26.002
57 27.999 38.345 38.648 37.54 36.678 37.229 36.502 35.608 36.755 35.884 36.235 35.752 36.576 36.447 37.459 38.021 37.233 27.147
58 29.613 38.708 39.247 38.129 37.209 36.357 39.021 37.833 36.68 37.998 36.402 36.402 36.853 36.468 37.412 36.69 37.718 38.254 29.669
59 27.141 37.798 37.474 37.945 38.964 38.895 40.325 39.722 37.705 38.908 39.344 36.964 38.409 38.084 37.215 36.896 36.533 36.634 37.857 29.267
60 27.359 37.461 38.493 38.462 37.858 38.267 39.446 38.353 40.698 39.424 38.949 40.58 43.623 39.892 38.098 38.064 39.465 40.036 40.409 40.225 28.492

特判 n = 1 n=1 n=1 时,答案为 0 0 0

  • x = 1 x=1 x=1 x = n x=n x=n 时,概率为 2 n \frac2n n2,期望为 1 2 n = n 2 \frac1{\frac2 n}=\frac n2 n21=2n
  • x ∈ [ 2 , n − 1 ] x\in[2,n-1] x[2,n1] 时,要询问到点 x x x 或与其相邻的两个点,总期望为 E 1 = 1 n ⋅ 0 + 2 n ⋅ n 2 + n − 3 n ⋅ E 1 + 1 E_1=\frac1n·0+\frac2n·\frac n2+\frac{n-3}n·E_1+1 E1=n10+n22n+nn3E1+1 解得 E 1 = 2 n 3 E_1=\frac{2n}3 E1=32n
    • 询问到了点 x x x 或与其相邻的两个点,概率为 1 n \frac1n n1,步数为 0 0 0
    • 只询问过了其中一个与 x x x 相邻的点,设期望为 E 2 E_2 E2,则 E 2 = 2 n ⋅ 0 + n − 2 n ⋅ E 2 + 1 E_2=\frac2n·0+\frac{n-2}n·E_2+1 E2=n20+nn2E2+1 解得 E 2 = n 2 E_2=\frac n2 E2=2n
    • 没问到任何一个与 x x x 相邻的点,重新进入该循环,概率为 n − 3 n \frac{n-3}n nn3,步数为 E 1 E_1 E1

综上,总期望为 E = 2 n ⋅ n 2 + n − 2 n ⋅ 2 n 3 = 2 n − 1 3 E=\frac2n·\frac n2+\frac{n-2}n·\frac{2n}3=\frac{2n-1}3 E=n22n+nn232n=32n1

const int mod = 998244353;
int inv3;

int qpow(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = (ll)res * a % mod;
        a = (ll)a * a % mod;
        b >>= 1;
    }
    return res;
}

int inv(int x) { return qpow(x, mod - 2); }

int solve() {
    int n; cin >> n;
    if (n == 1) return 0;
    return (ll)(n * 2 - 1) * inv(3) % mod;
}

int main() {
    inv3 = inv(3);
    FastIO
    Cases
    cout << solve() << endl;
    return 0;
}

12-Equalize the Array

题意 每次可以将数列里出现最多的数的所有这些数加一,求最终所有数能变成一样吗

题解 只要保证最小的数的数目为第一大即可(可以并列第一)

string solve() {
    int n; cin >> n;
    map<int, int> mp;
    set<int> st;
    for (int i = 0; i < n; i ++) {
        int x; cin >> x;
        st.insert(x);
        mp[x] ++;
    }
    int cnt = mp[*s.begin()];
    for (auto i : st)
        if (mp[i] > cnt)
            return no;
    return yes;
}

你可能感兴趣的:(OI,题解,算法,c++)