牛客练习赛137 题解更新至 3 题

Preface

哎呦我超真的,谁出的这场,怎么这么难,C后面的都不想补了,一点补的欲望都没有。

我会在代码一些有必要的地方加上注释,签到题可能一般就不会写了.

以下是代码火车头:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
#include <queue>
#include <map>
#include <unordered_map>
#include <iomanip>
#define endl '\n'
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rep2(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;

template<typename T>
void cc(const vector<T> &tem) {
    for (const auto &x: tem) cout << x << ' ';
    cout << endl;
}

template<typename T>
void cc(const T &a) { cout << a << endl; }

template<typename T1, typename T2>
void cc(const T1 &a, const T2 &b) { cout << a << ' ' << b << endl; }

template<typename T1, typename T2, typename T3>
void cc(const T1 &a, const T2 &b, const T3 &c) { cout << a << ' ' << b << ' ' << c << endl; }

void cc(const string &s) { cout << s << endl; }

void fileRead() {
#ifdef LOCALL
    freopen("D:\\AADVISE\\Clioncode\\untitled2\\in.txt", "r", stdin);
    freopen("D:\\AADVISE\\Clioncode\\untitled2\\out.txt", "w", stdout);
#endif
}

void kuaidu() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); }

inline int max(int a, int b) {
    if (a < b) return b;
    return a;
}

inline double max(double a, double b) {
    if (a < b) return b;
    return a;
}

inline int min(int a, int b) {
    if (a < b) return a;
    return b;
}

inline double min(double a, double b) {
    if (a < b) return a;
    return b;
}

void cmax(int &a, const int &b) { if (b > a) a = b; }
void cmin(int &a, const int &b) { if (b < a) a = b; }
void cmin(double &a, const double &b) { if (b < a) a = b; }
void cmax(double &a, const double &b) { if (b > a) a = b; }
using PII = pair<int, int>;
using i128 = __int128;
using vec_int = std::vector<int>;
using vec_char = std::vector<char>;
using vec_double = std::vector<double>;
using vec_int2 = std::vector<std::vector<int> >;
using que_int = std::queue<int>;

Problem A. A+B Problem

比较简单的问题,跟横滨区域赛似的,签到是个小dfs。

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
 
//--------------------------------------------------------------------------------
//struct or namespace:
 
//--------------------------------------------------------------------------------
char ans[N][5];
int len;
char A[5];
bool fl[5];
void dfs(int x) {
    if (x == n + 1) {
        ++len;
        rep(i, 1, n) ans[len][i] = A[i];
        return;
    }
    rep(i, 1, n) {
        if (fl[i]) continue;
        fl[i] = 1;
        A[x] = i + 'A' - 1;
        dfs(x + 1);
        fl[i] = 0;
    }
}
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    //cin >> T;
    while (T--) {
        cin >> n;
        dfs(1);
        rep(i, 1, len) {
            rep(j, 1, n) {
                cout << ans[i][j];
                if (j != n) cout << "+";
            }
            cout << " Problem" << endl;
        }
    }
    return 0;
}

Problem B. 图论

其实不是太难的样子,随便画画后大概知道,如果点集的大小是n,那么我们需要去掉n-1个边。

方案的话,大概就是点集里除了层数最小的那个点(如果有多个那就随便一个),其他的点和他们的父节点所连接的边。

去掉那些边,便可以使得他们互不相通了。

//--------------------------------------------------------------------------------
//struct or namespace:
namespace z {
    vector<PII> A[N];
    int son[N], dep[N], fa[N];
    int mmin = 0;
    bool fl[N];
    void dfs(const int x, const int pa) {
        son[x] = 1;
        dep[x] = dep[pa] + 1;
        fa[x] = pa;
        if (mmin == 0 and fl[x]) mmin = x;
        else if (dep[x] < dep[mmin] and fl[x]) mmin = x;
        for (auto [y, val] : A[x]) {
            if (y == pa) continue;
            dfs(y, x);
            son[x] += son[y];
 
        }
 
    }
 
    void clear(int n) {
        rep(i, 1, n) {
            A[i].clear();
        }
    }
 
    void add(int x, int y, int c = 1) {
        A[x].push_back({ y, c });
    }
};
//--------------------------------------------------------------------------------
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    //cin >> T;
    while (T--) {
        cin >> n >> m;
        z::clear(n);
        rep(i, 1, n - 1) {
            int a, b; cin >> a >> b;
            z::add(a, b);
            z::add(b, a);
        }
        vec<int> tem;
        rep(i, 1, m) {
            int a; cin >> a;
            tem.push_back(a);
            z::fl[a] = 1;
        }
        z::dfs(1, 0);
 
        cc(tem.size() - 1);
        // if (m == 1) continue;
        for (auto& x : tem) {
            if (x == z::mmin) continue;
            cc(x, z::fa[x]);
        }
 
    }
    return 0;
}

Problem C. 变化的数组(Easy Version)

我真的,哎,脑子就像是没带一样。困梦里小v一下,结果卡了好久。然后看了题解,发现其实还好,但是也还是思考了片刻。

首先我们知道,不能直接递推出来个式子去照着式子搞,因为有位运算的存在,都不是线性的。

然后是我们就正常来说dp去搞就可以了。

\[f_i=\sum_{i=0}{p_i*c_{i}} \]

\(p_i\)是在k次里有i次我们进行了变化的概率,这个可以直接算出来。而\(c_i\)是变化了i次后的值。

如果正常的暴力去写,dp的复杂度会爆掉。毕竟是n*k级别的。

所以这里有一个trick就是:因为a&m,实质上是只有a的前l位有影响(有影响就是会参与到&m的过程里)。(l:m的最高位,我们称前l位是从低到高l位)。

所以我们可以抽离开来,如果a>=(1<<(l+1)-1)的话(式子的含义就是说如果a是存在比m的最高位还要高的位置,也就是我们高位那些没有影响的位置),我们就直接加上剩下的那些部分就好了。

然后我们提前预处理出来前l位的变化,他们的范围不会超过(1<<(l+1))-1,也就是直接暴力跑一遍就好了,时间复杂度就变成了m*k。

//--------------------------------------------------------------------------------
const int N = 1e6 + 10;
const int M = 1e6 + 10;
const int mod = 998244353;
const int INF = 1e16;
int n, m, T;
int x, k;
int A[N];
//--------------------------------------------------------------------------------
//struct or namespace:
namespace ms {
 
    namespace ni {
        const int N = 5e3 + 10;
        vector<int> fact, infact;
 
        void cal_ni() {
            fact.resize(N + 5);
            infact.resize(N + 5);
            fact[0] = infact[0] = 1;
            for (int i = 1; i < N; i++)  fact[i] = fact[i - 1] * i % mod;
            infact[N - 1] = Kuai<mod>(fact[N - 1], mod - 2);
            for (int i = N - 2; i >= 1; i--) {
                infact[i] = infact[i + 1] * (i + 1) % mod;
            }
        }
    }

}
//--------------------------------------------------------------------------------
namespace ni = ms::ni;
 
int C(int n, int m) {
    return ni::fact[n] * ni::infact[m] % mod * ni::infact[n - m] % mod;
}
 
int dfs(int rt) {
    int res = rt;
    int ans = 0;
    auto kuai = Kuai<mod>;
    int fenmu = kuai(kuai(2, k), mod - 2);
    rep(i, 0, k) {
        ans += C(k, i) * fenmu % mod * res % mod;
        ans %= mod;
        res = res + (res & m) + x;
    }
    return ans;
}
int dp[5000 + 10];
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    //cin >> T;
    ni::cal_ni();
    while (T--) {
        cin >> n >> x >> m >> k;
        rep(i, 1, n) cin >> A[i];
        int lim = (1 << ((int)log2(m) + 1)) - 1;
        rep(i, 0, lim) {
            dp[i] = dfs(i);
        }
 
        int ans = 0;
        rep(i, 1, n) {
            int tem = dp[A[i] & lim];
            if (A[i] > lim) tem += A[i] - (A[i] & lim), tem %= mod;
            ans += tem;
            ans %= mod;
        }
        cc(ans);
    }
    return 0;
}

PostScript

我丢了,好难这一场,后面几个题赛时几乎都没有人过。

哎,我是彩笔。

posted @ 2025-04-25 02:30  AdviseDY  阅读(56)  评论(0)    收藏  举报