[题解]膜游赛Ⅱ
\[\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');
}