[题解]膜游赛Ⅱ

\[\color{red}{\text{校长者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?}} \\ \begin{array}{|} \hline \color{pink}{\text{The principal is really a god}} \\ \color{pink}{\text{with a closestool on the left and Yongshen on the right}} \\ \color{pink}{\text{holding a sharp pen to pierce the truth}} \\ \color{pink}{\text{Who can resist him? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{green}{\text{校長は本当に神であり、左側にトイレ、右側にヨンシェンがあり}} \\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{lightblue}{\text{Le principal est vraiment un dieu}} \\ \color{lightblue}{\text{avec des toilettes à gauche et Yongshen à droite}} \\ \color{lightblue}{\text{tenant un stylo pointu pour percer la vérité}} \\ \color{lightblue}{\text{Qui peut lui résister ? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{purple}{\text{Der Direktor ist wirklich ein Gott}} \\ \color{purple}{\text{mit einer Toilette links und Yongshen rechts}} \\ \color{purple}{\text{der einen spitzen Stift hält}} \\ \color{purple}{\text{um die Wahrheit zu durchdringen.}} \\ \color{purple}{\text{Wer kann ihm widerstehen? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{cyan}{\text{Principalis deus est, Yongshen a dextris cum latrina}} \\ \color{cyan}{\text{acuto stylo ad perforandum veritatem: quis resistet ei? }} \\ \hline \end{array} \\ \color{red}{\text{对曰:“无人,狗欲当之,还请赐教!”}} \\ \newcommand\brak[1]{\left({#1}\right)} \newcommand\Brak[1]{\left\{{#1}\right\}} \newcommand\d[0]{\text{d}} \newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor} \newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil} \newcommand\lcm[0]{\operatorname{lcm}} \newcommand\set[1]{\left\{{#1}\right\}} \newcommand\ceil[1]{\left\lceil{#1}\right\rceil} \newcommand\floor[1]{\left\lfloor{#1}\right\rfloor} \newcommand\rhs[1]{\;\text{Rhs}\;#1} \newcommand\lhs[1]{\;\text{Lhs}\;#1} \newcommand\Vec[1]{\vec{\mathbf{#1}}} \newcommand\rank[0]{\text{rank}} \]




  \(\mathcal{Back\;To\;The\;Menu}\).

2022-01-26 膜游赛Ⅱ

  今天都什么牛马题啊......

游戏测试 / Test

  设计 \(f(i,j)\) 表示从左到右枚举到位置 \(i\),有 \(j\) 个右边的已经填入 \([1,i]\),对于要填在左边的,考虑到端点的时候再计算其贡献。转移十分朴素,就不说了。时间复杂度 \(\mathcal O(n^2)\).

/** @author Arextre */

#include <bits/stdc++.h>

#define USING_FREAD
// #define NDEBUG
// #define NCHECK
#include <cassert>

namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void getmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void getmax(T& x, const T rhs) { x = std::max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
    if (x < 0) putchar('-'), x = -x;
    static int __stk[55], __bit = 0;
    do __stk[++__bit] = x % 10, x /= 10; while (x);
    while (__bit) putchar(__stk[__bit--] ^ 48);
    putchar(c);
}

} // namespace Elaina
using namespace Elaina;

const int mod = 1e9 + 7;
const int maxn = 5000;
inline void chkAdd(int& x, int y) { if ((x += y) >= mod) x -= mod; }

int inv[maxn + 5], fac[maxn + 5], finv[maxn + 5];
inline void prelude() {
    inv[0] = inv[1] = fac[0] = fac[1] = finv[0] = finv[1] = 1;
    rep (i, 2, maxn) {
        inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
        fac[i] = 1ll * fac[i - 1] * i % mod;
        finv[i] = 1ll * finv[i - 1] * inv[i] % mod;
    }
}
inline int A(int n, int m) {
    if (n < m) return 0;
    return 1ll * fac[n] * finv[n - m] % mod;
}

int lef[maxn + 5], rig[maxn + 5], n;

inline void input() {
    readin(n); int x;
    rep (i, 1, n) {
        readin(x);
        if (x > 0) ++lef[x];
        else ++rig[-x];
    }
}

int dp[maxn + 5][maxn + 5];
signed main() {
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
    prelude();
    input();
    dp[0][0] = 1;
    int pre = 0, cnt = 0;
    rep (i, 1, n) {
        pre += rig[i];
        for (int j = 0; j < i; ++j) {
            // residual that need to stay on the right
            if (j < pre) chkAdd(dp[i][j + 1], 1ll * dp[i - 1][j] * (pre - j) % mod);
            chkAdd(dp[i][j], dp[i - 1][j]);
        }
        for (int j = 0; j <= i; ++j)
            dp[i][j] = 1ll * dp[i][j] * A(i - j - cnt, lef[i]) % mod;
        cnt += lef[i];
    }
    writln(dp[n][pre]);
    return 0;
}

游戏贸易 / Trade

  思路十分显然,分都摆在那里了,就看你打不打了。发现每个物品的期望实际上是一个以路径条数为系数的多项式:

\[\begin{aligned} v_i&=\sum_{j=0}^dpd_j\times \brak{p_i^j(b_i-a_i)-(1-p^i)a_i} \\ &=\sum_{j=0}^dpd_j\times (b_ip_i^j-a_i) \\ &=b_i\brak{\sum_{j=0}^d pd_jp_i^j}-a_i \\ &=b_if(p_i)-a_i \\ f(x)&\overset\Delta=\sum_{i=0}^d pd_i\cdot x^i \end{aligned} \\ \]

  那么思路很显然,先用 \(\rm DS\) 求出路径条数,再用多项式多点求值就好了。就这俩玩意我调了一上午 😦 我打的多点求值是暴力取模,常数有点大,但是还是可以过。如果有时间就去看一看那个不需要取模的新方法。

  时间复杂度 \(\mathcal O(n\log^2n)\).

/** @author Arextre */

#include <bits/stdc++.h>
using namespace std;

#define USING_FREAD
// #define NDEBUG
// #define NCHECK
#include <cassert>

namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void getmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void getmax(T& x, const T rhs) { x = std::max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
    if (x < 0) putchar('-'), x = -x;
    static int __stk[55], __bit = 0;
    do __stk[++__bit] = x % 10, x /= 10; while (x);
    while (__bit) putchar(__stk[__bit--] ^ 48);
    putchar(c);
}

} // namespace Elaina
using namespace Elaina;

const int maxn = 1e5;
const int pri_root = 3;
const int mod = 998244353;
inline void chkAdd(int& x, int y) { if ((x += y) >= mod) x -= mod; }

typedef vector<int> poly;

inline int qkpow(int a, int n) {
    int ret = 1;
    for (; n; n >>= 1, a = 1ll * a * a % mod)
        if (n & 1) ret = 1ll * ret * a % mod;
    return ret;
}

namespace NTT {

int rev[maxn * 10 + 5], G[55], n, invn;
/** @warning don't forget to invoke it */
inline void init() {
    for (int i = 1; i <= 50; ++i)
        G[i] = qkpow(pri_root, (mod - 1) / (1 << i));
}

inline void prepare(int len) {
    for (n = 1; n < len; n <<= 1);
    invn = qkpow(n, mod - 2);
    for (int i = 0; i < n; ++i)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1)? (n >> 1): 0);
}
inline void trans(poly& f, int opt) {
    f.resize(n);
    for (int i = 0; i < n; ++i) if (i < rev[i])
        swap(f[i], f[rev[i]]);
    for (int p = 2, lev = 1; p <= n; p <<= 1, ++lev) {
        int len = p >> 1, w = G[lev];
        for (int k = 0; k < n; k += p) {
            int buf = 1, tmp;
            for (int i = k; i < k + len; ++i, buf = 1ll * buf * w % mod) {
                tmp = 1ll * f[i + len] * buf % mod;
                f[i + len] = (f[i] + mod - tmp) % mod;
                f[i] = (f[i] + tmp) % mod;
            }
        }
    }
    if (opt != 1) {
        if ((int)(f.size()) > 1) reverse(f.begin() + 1, f.end());
        for (int i = 0; i < n; ++i)
            f[i] = 1ll * f[i] * invn % mod;
    }
    return ;
}

inline poly convolution(poly f, poly g) {
    int len;
    prepare(len = (int)(f.size() + g.size()) - 1);
    trans(f, 1), trans(g, 1);
    for (int i = 0; i < n; ++i) f[i] = 1ll * f[i] * g[i] % mod;
    trans(f, 1433223);
    f.resize(len);
    return f;
}

} // namespace NTT;

namespace _poly {

poly polyinv(poly f, int n) {
    f.resize(n);
    if (n == 1) return { qkpow(f[0], mod - 2) };
    poly g = polyinv(f, n >> 1);
    NTT::prepare(n << 1);
    NTT::trans(f, 1), NTT::trans(g, 1);
    for (int i = 0; i < NTT::n; ++i)
        g[i] = 1ll * g[i] * (2 + mod - 1ll * f[i] * g[i] % mod) % mod;
    NTT::trans(g, 1919810); g.resize(n);
    return g;
}
poly Inv(poly f, int len) {
    f.resize(len);
    f = polyinv(f, 1 << __lg(len) + 1);
    f.resize(len);
    return f;
}

inline poly Mod(poly a, poly d) {
    int n = a.size(), m = d.size();
    if (n < m) return a;
    reverse(a.begin(), a.end());
    reverse(d.begin(), d.end());
    poly b = NTT::convolution(a, Inv(d, n - m + 1));
    b.resize(n - m + 1);
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    reverse(d.begin(), d.end());
    poly r = NTT::convolution(b, d);
    r.resize(m - 1);
    for (int i = 0; i < m - 1; ++i)
        r[i] = (a[i] + mod - r[i] % mod) % mod;
    return r;
}

} // namespace _poly;

int n, m;
vector<int> g[maxn + 5];
int a[maxn + 5], b[maxn + 5], p[maxn + 5];

inline void input() {
    readin(n, m);
    int u, v;
    for (int i = 1; i < n; ++i) {
        readin(u, v);
        g[u].push_back(v), g[v].push_back(u);
    }
    for (int i = 1; i <= m; ++i) {
        readin(a[i], b[i], u, v);
        p[i] = (1ll + mod - 1ll * u * qkpow(v, mod - 2) % mod) % mod;
    }
}

int dis[maxn + 5];
namespace DS {

int used[maxn + 5], siz[maxn + 5], mxx[maxn + 5];
void findrt(int u, int par, int n, int& rt) {
    siz[u] = 1, mxx[u] = 0;
    for (const int& v: g[u]) if (v != par && !used[v]) {
        findrt(v, u, n, rt), siz[u] += siz[v];
        getmax(mxx[u], siz[v]);
    }
    getmax(mxx[u], n - siz[u]);
    if (mxx[u] < mxx[rt]) rt = u;
}
poly cur, buc; int mxdep;
void dfs(int u, int par, int dep) {
    siz[u] = 1;
    if (dep > mxdep) ++mxdep, buc.push_back(1);
    else ++buc[dep];
    for (const int& v: g[u]) if (v != par && !used[v])
        dfs(v, u, dep + 1), siz[u] += siz[v];
}
inline void calc(int u) {
    int len = 0; siz[u] = 1;
    cur.clear(); cur.push_back(1);
    for (const int& v: g[u]) if (!used[v]) {
        mxdep = 0; buc.clear(); buc.push_back(0);
        dfs(v, u, 1); siz[u] += siz[v];
        if (len < mxdep) cur.resize(1 + (len = mxdep));
        for (int i = 0; i <= mxdep; ++i) chkAdd(cur[i], buc[i]); 
        buc = NTT::convolution(buc, buc);
        for (int i = 0; i <= (mxdep << 1); ++i)
            chkAdd(dis[i], (mod - buc[i]) % mod);
    }
    cur = NTT::convolution(cur, cur);
    for (int i = 0; i <= (len << 1); ++i)
        chkAdd(dis[i], cur[i]);
}
void launch(int, int);
void divide(int u) {
    used[u] = true; calc(u);
    for (const int& v: g[u]) if (!used[v])
        launch(v, siz[v]);
}
void launch(int u, int n) {
    int rt = 0; mxx[0] = n + 1;
    findrt(u, 0, n, rt);
    divide(rt);
}

} // namespace DS;

poly f;
inline void buildPoly() {
    int s = 0;
    for (int i = 0; i <= n; ++i) chkAdd(s, dis[i]);
    s = qkpow(s, mod - 2);
    f.resize(n);
    for (int i = 0; i < n; ++i)
        f[i] = 1ll * dis[i] * s % mod;
    // for (int i = 0; i < n; ++i)
    //     printf("%d ", f[i]); Endl;
}

poly d[maxn << 2 | 2];
void prelude(int i, int l, int r) {
    if (l == r) return d[i] = { (mod - p[l]) % mod, 1 }, void();
    prelude(i << 1, l, l + r >> 1);
    prelude(i << 1 | 1, (l + r >> 1) + 1, r);
    d[i] = NTT::convolution(d[i << 1], d[i << 1 | 1]);
}

int ans[maxn + 5];
void Evaluation(poly f, int i, int l, int r) {
    f = _poly::Mod(f, d[i]);
    if (l == r) return ans[l] = f[0], void();
    Evaluation(f, i << 1, l, l + r >> 1);
    Evaluation(f, i << 1 | 1, (l + r >> 1) + 1, r);
}

signed main() {
    NTT::init();
    freopen("trade.in", "r", stdin);
    freopen("trade.out", "w", stdout);
    input();
    DS::launch(1, n);
    buildPoly();
    prelude(1, 1, m);
    Evaluation(f, 1, 1, m);
    for (int i = 1; i <= m; ++i)
        printf("%d\n", (1ll * b[i] * ans[i] % mod + mod - a[i]) % mod);
    return 0;
}

游戏密码 / Password

  真的阴间,不过今天的题可能真的就只有这一道值得分析一下了......

  实际上,题目就是想让我们求一个线性变换 \(f:\vec a\rightarrow \vec b\),并且这个变换的某些位置是固定的。对于 \(01\) 串,经典真的是经典吗?的处理方法是通过 \(01\) 矩阵进行的,也就是说,我们可以构造一个 \(m\times n\) 的矩阵 \(F\),使得 \(F\vec a=\vec b\). 传递的信息,就是 \(\vec b\) 实际上是 \(\vec a\) 经过哪些变换叠加而成,而解密的时候进行逆运算即可。

  至于 \(F\) 应当如何传递?使用伪随机函数通过同一个种子进行随机即可。过程中使用 bitset 进行优化,复杂度为 \(\mathcal O\brak{\frac{n^3}{\omega}}\).

/** @author Arextre */
#include "password.h"
#include <bits/stdc++.h>
using namespace std;

const int maxm = 2050;
const int maxn = 1000;

bitset<maxn + 5> v[maxm + 5];
bitset<maxn + 5> x[maxm + 5]; // linear algebra
bitset<maxm + 5> used[maxm + 5];

inline void insert(bitset<maxn + 5> s, int id, int b) {
    bitset<maxm + 5> vis; vis[id] = 1;
    for (int i = b - 1; ~i; --i) if (s[i]) {
        if (x[i] == 0) {
            x[i] = s, used[i] = vis;
            return;
        }
        s ^= x[i], vis ^= used[i];
    }
}

inline void encoder(int n, int m, int k, const char* a, const char* b, char* ans) {
    mt19937 rnd(1919810);
    for (int i = 0; i < m; ++i)
        for (int j = 0; j < n; ++j)
            v[i][j] = rnd() & 1;
    bitset<maxn + 5> A;
    for (int i = 0; i < n; ++i) A[i] = (a[i] == '1');
    bitset<maxm + 5> Ans;
    for (int i = 0; i < m; ++i) {
        if (b[i] == '?') insert(v[i], i, n);
        else if (b[i] == '1') A ^= v[i], Ans[i] = 1;
    }
    for (int i = n - 1; ~i; --i) if (A[i])
        A ^= x[i], Ans ^= used[i];
    for (int i = 0; i < m; ++i)
        ans[i] = (Ans[i] == 0? '0': '1');
}
inline void decoder(int n, int m, const char* a, char* ans) {
    mt19937 rnd(1919810);
    for (int i = 0; i < m; ++i)
        for (int j = 0; j < n; ++j)
            v[i][j] = rnd() & 1;
    bitset<maxn + 5> A;
    for (int i = 0; i < m; ++i) if (a[i] == '1')
        A ^= v[i];
    for (int i = 0; i < n; ++i)
        ans[i] = (A[i] == 0? '0': '1');
}
posted @ 2022-03-14 21:47  Arextre  阅读(26)  评论(0编辑  收藏  举报