2020 Multi-University Training Contest 5

Contest Info


传送门

Solved A B C D E F G H I J K L M
6 / 13 O - O - - - Ø Ø O - - Ø -
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. Tetrahedron

签到。

C. Boring Game

找到规律后直接模拟即可。
规律是多种多样的。

Code
// Author : heyuhhh
// Created Time : 2020/08/04 13:57:50
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<vector<int>> mytype;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
  void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
  void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
  void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 1e6 + 5;

int n, k;
int p[N];

mytype cz(vector<int>& a, vector<int>&b, int Max) {
    mytype res;
    vector<int> t1 = b, t2 = a;
    for (int i = 0; i < 2 * n; i++) {
        a[i] += Max;
        b[i] += Max;
    }
    res.push_back(a);
    res.push_back(t1);
    res.push_back(t2);
    res.push_back(b);
    return res;
}

void gao(mytype& a, int k, int Max) {
    if (k == 0) return;
    vector<mytype> res;
    for (int i = 0; i < sz(a); i += 2) {
        res.push_back(cz(a[i], a[i + 1], Max));
    }
    a.clear();
    for (auto& it : res) {
        for (auto& it2 : it) {
            a.push_back(it2);
        }
    }
    gao(a, k - 1, Max * 2);
}

void run() {
    cin >> n >> k;
    int tot = 2 * n;
    for (int i = 1; i <= k; i++) {
        tot *= 2;
    }
    for (int i = 1; i <= tot; i++) {
        cin >> p[i];
    }
    mytype a;
    a.resize(2);
    a[0].resize(2 * n), a[1].resize(2 * n);
    for (int i = 0; i < 2 * n; i++) {
        a[0][i] = 2 * n - i;
        a[1][i] = 2 * n + 1 + i;
    }
    --k;
    gao(a, k, 4 * n);
    for (int i = 0; i < 2 * n; i++) {
        for (int j = 0; j < sz(a); j++) {
            cout << p[a[j][i]];
            if (--tot > 0) {
                cout << ' ';
            }
        }
    }
    cout << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0); 
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

G. Tree

与选择边数有关的树形\(dp\),一般的写法就是一开始钦定与父亲连着的拿一条边要选,这样方便转移。最后统计答案断开这条边即可。
这个题设\(dp[i][0/1]\)表示以\(i\)为根节点的子树中,是否存在一个点其度数不受到限制,并且\(i\)与其父亲这条边已选的最大边权和。
那么最后的答案就是\(dp[i][1]\)断开父亲这条边的最大值,但是注意与父亲断开过后可能还能够多选择一个儿子,这种情况再单独考虑一下即可。
因为dp转移时要贪心进行转移,所以代码有点细节:

Code
// Author : heyuhhh
// Created Time : 2020/08/04 19:16:59
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
  void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
  void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
  void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 1e5 + 5;
void run() {
    int n, k;
    cin >> n >> k;
    vector<vector<pii>> G(n);
    vector<int> d(n);
    ll sum = 0;
    for (int i = 0; i < n - 1; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        sum += w;
        --u, --v;
        G[u].push_back(MP(v, w));
        G[v].push_back(MP(u, w));
        ++d[u], ++d[v];
    }
    if (*max_element(all(d)) <= k) {
        cout << sum << '\n';
        return;
    }
    if (k == 0) {
        cout << 0 << '\n';
        return;
    }
    if (k == 1) {
        ll ans = 0;
        for (int i = 0; i < n; i++) {
            ll res = 0;
            for (auto it : G[i]) {
                int v = it.fi, w = it.se;
                res += w;
            }
            ans = max(ans, res);
        }
        cout << ans << '\n';
        return;
    }

    vector<vector<ll>> dp(n, vector<ll>(2));

    function<ll(int, int, int)> calc = [&](int u, int c, int fa) {
        vector<pair<ll, pair<int, int>>> dp0;
        for (auto it : G[u]) {
            int v = it.fi, w = it.se;
            if (v != fa) {
                dp0.push_back(MP(dp[v][0] + w, MP(v, w)));
            }
        }
        sort(dp0.rbegin(), dp0.rend());
        ll res = 0, Max = -1e18;
        for (int i = 0; i < min(sz(dp0), c); i++) {
            res += dp0[i].fi;
        }
        for (int i = c; i < sz(dp0); i++) {
            int v = dp0[i].se.fi, w = dp0[i].se.se;
            Max = max(Max, dp[v][1] + w);
        }
        if (sz(dp0) >= c + 1) {
            for (int i = 0; i < c; i++) {
                int v = dp0[i].se.fi;
                Max = max(Max, -dp[v][0] + dp[v][1] + dp0[c].fi);
            }
        } else {
            for (int i = 0; i < sz(dp0); i++) {
                int v = dp0[i].se.fi;
                Max = max(Max, -dp[v][0] + dp[v][1]);
            }
        }
        res += Max;
        return res;
    };

    ll ans = 0;

    function<void(int, int)> dfs = [&](int u, int fa) {
        vector<pair<ll, int>> dp0;
        for (auto it : G[u]) {
            int v = it.fi, w = it.se;
            if (v != fa) {
                dfs(v, u);
                dp[u][1] += dp[v][0] + w;
                dp0.push_back(MP(dp[v][0] + w, v));
            }
        }
        sort(dp0.rbegin(), dp0.rend());
        for (int i = 0; i < min(sz(dp0), k - 1); i++) {
            dp[u][0] += dp0[i].fi;
        }
        dp[u][1] = max(dp[u][1], calc(u, k - 2, fa));
        ans = max(ans, max(dp[u][1], calc(u, k - 1, fa)));
    };

    dfs(0, 0);
    cout << ans << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
    freopen("output.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

H. Set1

题意:
同Set2类似,不过是每次删除最小的一个后再随机删除\(k-1\)个。如果个数小于\(k\)就不删除。然后问每个数剩下的概率。
范围有点不同:\(n\leq 5000\)

思路:
首先看范围就能明确这个题可以使用\(O(n^2)\)的做法,一般就是dp。
这个题可以类似于约瑟夫环的删除那样去思考,我们定义\(dp[n][i]\)表示最后还有\(n\)个数,第\(i\)个最终没被删去的概率。那么接下来就开始删,首先第一个默认要被删去,然后会随机进行删除,假设左边删掉\(t\)个,那么\(i\)这个位置就会变到\(i-t-1\)这个位置。后面的是没影响的。据此我们可以写出\(dp\)转移式。

\[dp[i][j]=\sum_{t=0}^{i-2}\frac{{i-2\choose t}\cdot{n-i\choose k-t}}{n-1\choose k}dp[i-k-1][j-t-1] \]

初一看复杂度是\(O(n^2k)\)的,但实际上对于第一维只有\(O(\frac{n}{k})\)个状态有用。
那么直接跑上述dp复杂度实际上是\(O(n^2)\)的。

Code
// Author : heyuhhh
// Created Time : 2020/08/05 16:45:03
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5000 + 5, MOD = 998244353;

int qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;   
    }
    return res;   
}
int fac[N], inv[N];
void init() {
    fac[0] = 1;
    for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[N - 1] = qpow(fac[N - 1], MOD - 2);
    for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}

int C(int n, int m) {
    return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}

void run() {
    int n, k;
    cin >> n >> k;
    if (n <= k) {
        for (int i = 1; i <= n; i++) {
            cout << 1 << " \n"[i == n];
        }
        return;
    }
    int r = n % (k + 1);
    if (r == 0) {
        for (int i = 1; i <= n; i++) {
            cout << 0 << " \n"[i == n];
        }
        return;
    }
    vector<int> ans(n + 1);
    vector<vector<int>> dp(n + 1, vector<int>(n + 1));
    for (int i = 1; i <= r; i++) {
        dp[r][i] = 1;
    }
    vector<int> pre(n + 1);
    for (int i = k; i <= n; i++) {
        pre[i] = qpow(C(i, k), MOD - 2);
    }   
    for (int i = r + k + 1; i <= n; i += k + 1) {
        for (int j = 2; j <= i; j++) {
            int L = j - 2, R = i - j;
            for (int t = 0; t <= k; t++) {
                if (t <= L && k - t <= R) {
                    dp[i][j] = (dp[i][j] + 1ll * C(L, t) * C(R, k - t) % MOD * pre[i - 1] % MOD
                     * dp[i - k - 1][j - t - 1] % MOD) % MOD;
                }
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        cout << dp[n][i] << " \n"[i == n];
    }
}

int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    init();
    int T; cin >> T; while(T--)
    run();
    return 0;
}

I. Paperfolding

显然我们可以将所有的折纸方案数归为只有两种的情况。
并且每折一次,对应方向的折现都会翻倍。
因为求的是期望,所以与顺序无关,我们可以直接枚举横向折多少次、竖向折多少次,然后直接根据期望定义统计答案即可。

Code
// Author : heyuhhh
// Created Time : 2020/08/04 12:49:25
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 998244353;

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

void run() {
    ll n;
    cin >> n;
    int res = 1ll * 2 * qpow(3, n) % MOD;
    res = (res + qpow(2, n)) % MOD;
    res = (res + qpow(4, n)) % MOD;
    res = 1ll * res * qpow(qpow(2, n), MOD - 2) % MOD;
    cout << res << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

L. Set1

题意:
给定一个\(1\)~\(n\)的集合,保证\(n\)为奇数。
然后每次首先删除最小的一个数,之后再在剩下的数中随机删除一个数直到还剩下\(1\)个数。
现在对于每个数\(i\),回答数\(i\)剩下的概率。

思路:
\(p=\frac{n}{2}\),首先有观察:

  • 对于一个数\(i\),删除的最小的\(p\)个数一定在他左边,他右边的数一定是被随机删掉的。

接下来考虑枚举所有情况,现在我们知道其右边有\(n-i\)个数,他们一定会和左边的\(n-i\)个一操作进行配对,然后剩下的两两进行配对。
先考虑第一种,左右互相配对的方案数:也就是我们要从左边选出\(n-i\)个位置,然后还要乘以一个阶乘,即为\({i-1\choose n-i}\cdot (n-i)!\)
第二种就是剩下\(i-1-(n-i)\)个两两匹配,那么答案就是\(\sum_{j=0}{i-1-(n-i)-2j\choose 2}\)。注意最后还要除以一个阶乘,因为顺序是无关的。
那么将两者乘起来就是答案。

Code
// Author : heyuhhh
// Created Time : 2020/08/04 16:15:23
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5e6 + 5, MOD = 998244353;

int qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;   
    }
    return res;   
}
int fac[N], inv[N];
void init() {
    fac[0] = 1;
    for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[N - 1] = qpow(fac[N - 1], MOD - 2);
    for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}

int C(int n, int m) {
    return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}

int cat(int n) {
    if (n == 0) return 1;
    return 1ll * C(2 * n, n) * qpow(n + 1, MOD - 2) % MOD;
}

int ans[N];

void run() {
    int n;
    cin >> n;
    int k = n / 2;
    for (int i = 1; i <= k; i++) {
        ans[i] = 0;
    }
    for (int i = k + 1; i <= n; i++) {
        int p = i - 1 - (n - i);
        int res = 1ll * C(i - 1, n - i) % MOD * fac[n - i] % MOD;
        int res2 = 1ll * fac[p] * qpow(1ll * qpow(2, p / 2) * fac[p / 2] % MOD, MOD - 2) % MOD;
        res = 1ll * res * res2 % MOD;
        ans[i] = res;
    }
    int tot = 0;
    for (int i = 1; i <= n; i++) {
        tot += ans[i];
        if (tot >= MOD) tot -= MOD;
    }
    tot = qpow(tot, MOD - 2);
    for (int i = k; i <= n; i++) {
        ans[i] = 1ll * ans[i] * tot % MOD;
    }
    for (int i = 1; i <= n; i++) {
        cout << ans[i] << " \n"[i == n];
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    init();
    int T; cin >> T; while(T--)
    run();
    return 0;
}
posted @ 2020-08-09 16:06  heyuhhh  阅读(226)  评论(0编辑  收藏  举报