Goodbye Jihai 部分题目简要题解
从这里开始
好像那天正好在路上,成功错过了打(掉)比赛(rating)的好机会。
(据可靠消息称,神仙 jerome_wei 不走水就捧杯了。
因为我不太会二次剩余,所以现在还没补 E。
Problem A 新年的促销
dp 即可。
不难注意到假设最终一共带走了 $k$ 袋大米,那么购买的一定是其中价格最小的若干袋。
考虑枚举买走的大米的最大价值,记录一下买了多少袋,以及花了多少钱,然后就计算答案了。
时间复杂度 $O(n^2m)$
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
#define ll long long
const int N = 305, M = 1e3 + 5;
const ll llf = (signed ll) (~0ull >> 3);
typedef class Item {
public:
int w, p;
} Item;
int n, m, a, b;
Item it[N];
ll ans[M];
ll f[N][M];
int main() {
scanf("%d%d%d%d", &n, &m, &a, &b);
for (int i = 1; i <= n; i++) {
scanf("%d", &it[i].w);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &it[i].p);
}
sort(it + 1, it + n + 1, [&] (const Item& a, const Item& b) { return a.w > b.w; });
for (int i = 0; i <= m; i++)
ans[i] = -llf;
for (int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++)
f[i][j] = -llf;
f[0][0] = 0;
for (int i = 1; i <= n; i++) {
int id = i;
for (int j = i + 1; j <= n; j++)
if (it[j].p < it[id].p)
id = j;
rotate(it + i, it + id, it + id + 1);
for (int j = i; j; j--) {
for (int k = m; k >= it[i].p; k--) {
f[j][k] = max(f[j][k], f[j - 1][k - it[i].p] + it[i].w);
}
}
ll sum = 0;
int cnt = 0;
for (int j = 1; j <= i; j++) {
int x = (j / a) + (j / b);
while (i + cnt < n && cnt < x)
sum += it[i + ++cnt].w;
for (int k = 1; k <= m; k++) {
ans[k] = max(ans[k], sum + f[j][k]);
}
}
}
ans[0] = 0;
for (int i = 1; i <= m; i++) {
ans[i] = max(ans[i], ans[i - 1]);
printf("%lld ", ans[i]);
}
return 0;
}
Problem B 新年的新航线
可以猜想 $n > 3$ 一定有解。
考虑度为 $2$ 的点 $p$,与它相邻的点是 $u, v$,那么它在生成树上的度数只可能为 1。那么相当于会对 $u, v$ 中其中一个点的度数加上 1。因为是三角剖分,所以一定存在边 $(u, v)$,我们给 $(u, v)$ 标上数 $i$。
所以现在的问题是给一个边界上有标数的多边形找一个生成树,满足每个点的度数要么为 1,要么至少为 3。简单讨论一下可以发现 $n = 4$ 始终有解。考虑不断减少一个 $n > 4$ 的多边形的点数。
仍然考虑 2 度点 $p$,考虑仍然硬点它和其他在多边形上的点恰好有 1 条边。不妨设和它相邻的点是 $u, v$。
- 如果 $(p, u), (p, v)$ 无标数,那么给 $(u, v)$ 标上数 $p$,然后删掉 $p$。
- 如果 $(p, u)$ 有标数 $s$,$(p, v)$ 无标数,那么连上边 $(s, u), (p, u)$,然后删掉 $p$
- 如果 $(p, u), (p, v)$ 有标数,那么它们标数均向 $p$ 连边,然后给 $(u, v)$ 标上 $p$,再删掉 $p$
不难做到线性。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
const int N = 1e6 + 5, N2 = N << 1;
#define pii pair<int, int>
int n;
int d[N];
boolean ban[N2];
int us[N2], vs[N2];
vector<int> G[N];
vector<pii> lab[N];
vector<pii> ans;
int main() {
scanf("%d", &n);
if (n == 3) {
puts("-1");
return 0;
}
int ce = 0;
for (int i = 1; i <= n; i++) {
int u = i;
int v = (i == n) ? (1) : (i + 1);
++ce;
d[u]++, d[v]++;
us[ce] = u, vs[ce] = v;
G[u].push_back(i);
G[v].push_back(i);
}
for (int i = 1, u, v; i <= n - 3; i++) {
scanf("%d%d", &u, &v);
++ce;
us[ce] = u, vs[ce] = v;
G[u].push_back(ce);
G[v].push_back(ce);
d[u]++, d[v]++;
}
queue<int> Q;
for (int i = 1; i <= n; i++) {
if (d[i] == 2) {
Q.push(i);
}
}
for (int _ = 1; _ <= n - 4; _++) {
int p = Q.front();
Q.pop();
vector<int> tmp;
for (auto eid : G[p]) {
if (!ban[eid]) {
tmp.push_back(eid);
}
}
G[p].clear();
assert(tmp.size() == 2u);
int ex = tmp[0], ey = tmp[1];
int u = us[ex] ^ vs[ex] ^ p;
int v = us[ey] ^ vs[ey] ^ p;
int lx = 0, ly = 0;
for (auto par : lab[p]) {
int e = par.first;
int w = par.second;
if (e == u) {
lx = w;
} else if (e == v) {
ly = w;
}
}
ban[ex] = ban[ey] = true;
lab[p].clear();
if (lx && ly) {
ans.emplace_back(lx, p);
ans.emplace_back(ly, p);
lab[u].emplace_back(v, p);
lab[v].emplace_back(u, p);
} else if (!lx && !ly) {
lab[u].emplace_back(v, p);
lab[v].emplace_back(u, p);
} else if (lx && !ly) {
ans.emplace_back(lx, u);
ans.emplace_back(p, u);
} else {
ans.emplace_back(ly, v);
ans.emplace_back(p, v);
}
if (--d[u] == 2)
Q.push(u);
if (--d[v] == 2)
Q.push(v);
}
vector<int> rest;
for (int i = 1; i <= n; i++) {
if (!G[i].empty()) {
rest.push_back(i);
}
}
sort(rest.begin(), rest.end(), [&] (int x, int y) { return d[x] > d[y]; });
assert(rest.size() == 4u);
auto have_nearby = [&] (int x) {
for (auto y : lab[x]) {
int e = y.first;
if (!G[e].empty()) {
return true;
}
}
return false;
};
auto link_nearby = [&] (int p) {
for (auto y : lab[p]) {
int e = y.first;
if (G[e].empty())
continue;
int q = y.second;
ans.emplace_back(p, q);
}
};
boolean flag0 = have_nearby(rest[0]);
boolean flag1 = have_nearby(rest[1]);
if (flag0 && flag1) {
link_nearby(rest[0]);
link_nearby(rest[1]);
ans.emplace_back(rest[0], rest[1]);
ans.emplace_back(rest[2], rest[0]);
ans.emplace_back(rest[3], rest[1]);
} else if (flag0) {
link_nearby(rest[0]);
ans.emplace_back(rest[0], rest[1]);
ans.emplace_back(rest[0], rest[2]);
ans.emplace_back(rest[0], rest[3]);
} else {
link_nearby(rest[1]);
ans.emplace_back(rest[1], rest[0]);
ans.emplace_back(rest[1], rest[2]);
ans.emplace_back(rest[1], rest[3]);
}
assert((signed) ans.size() == n - 1);
for (auto par : ans) {
printf("%d %d\n", par.first, par.second);
}
return 0;
}
Problem C 新年的复读机
相信三方的区间 dp 大家都会。
如果 $n = 1$ 的时候答案为 0。
如果 $n > 1$ ,每个数一定会对答案有贡献,考虑原来的数合并的时候不会新产生贡献。考虑合并 gcd 为 $g_1$ 和 $g_2$ 的两段,它们的长度都至少为 2,两段的长度分别为 $l_1, l_2$,不妨设 $g_1 \leqslant g_2$,那么至少会额外花费 $g_1 + g_2 (l_2 - 1)$ 的代价,而考虑不断将第二段中的数依次加入第一段中,至多会花费 $g_1 l_2$ 的代价。
因此存在一种最优策略是从一个点开始,每次不断将左侧或者右侧的点加进来。
不难发现向一边拓展的时候一定会拓展到 gcd 发生变化为止,否则做一些简单调整可以让答案更优。
然后粗略分析得到了状态数为 $O(n\log^2 V)$ 的 dp 做法。
冷静一下仔细分析发现,每个状态 $[l, r]$ 一定满足要么 $l = 1$ 或者 $r = n$,要么 $r$ 是从 $l$ 开始的前缀 gcd 发生变化的位置,要么 $l$ 是从 $r$ 开始的后缀 gcd 发生变化的位置。因此状态数为 $O(n\log V)$。
经过一些简单预处理能够让转移做到 $O(1)$。
常数太大,卡不过去,sad.....
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
const int N = 2e5 + 5, M = 21000000;
#define ll long long
#define pil pair<int, ll>
#define pii pair<int, int>
const ll llf = (signed ll) (~0ull >> 2);
template <typename T>
T gcd(T a, T b) {
return (!b) ? a : gcd(b, a % b);
}
template <typename T>
void vmin(T& a, T b) {
(a > b) && (a = b);
}
typedef class Status {
public:
int l, r;
ll v, f;
Status *chl, *chr;
Status() { }
Status(int l, int r, ll v) : l(l), r(r), v(v), f(llf) { }
void update() {
(chl) && (vmin(chl->f, f + (l - chl->l) * v), 0);
(chr) && (vmin(chr->f, f + (chr->r - r) * v), 0);
}
} Status;
int n;
ll a[N];
Status pl[M], *top = pl;
vector<Status*> pre[N], suf[N];
Status* get(int l, int r, ll v) {
return *top = Status(l, r, v), top++;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", a + i);
}
for (int i = 1; i <= n; i++) {
pre[i].push_back(get(i, i, a[i]));
ll v = a[i];
for (auto it = pre[i - 1].rbegin(); it != pre[i - 1].rend(); it++) {
int pos = (*it)->l;
v = gcd((*it)->v, v);
if (v ^ pre[i].back()->v)
pre[i].push_back(get(pos, i, v));
}
if (pre[i].back()->l != 1)
pre[i].push_back(get(1, i, v));
reverse(pre[i].begin(), pre[i].end());
Status *tmp = NULL;
for (auto x : pre[i])
x->chl = tmp, tmp = x;
}
for (int i = n; i; i--) {
suf[i].emplace_back(pre[i].back());
ll v = a[i];
for (auto it : suf[i + 1]) {
int pos = it->r;
v = gcd(v, it->v);
if (v != suf[i].back()->v)
suf[i].push_back(get(i, pos, v));
}
if (suf[i].back()->r != n)
suf[i].push_back(get(i, n, v));
Status* tmp = NULL;
for (auto it = suf[i].rbegin(); it != suf[i].rend(); it++)
(*it)->chr = tmp, tmp = *it;
}
assert(top - pl < M);
vector<int> h (n + 1, -1);
vector<Status*> G;
vector<int> nxt;
for (int i = n; i; i--) {
for (auto x : pre[i]) {
G.push_back(x);
nxt.push_back(h[x->l]);
h[x->l] = (signed) G.size() - 1;
}
}
for (int i = 1; i <= n; i++) {
auto it = suf[i].begin();
auto _it = suf[i].end();
for (int _ = h[i]; ~_; _ = nxt[_]) {
auto& t = G[_];
while (it != _it && (*it)->r <= t->r)
it++;
t->chr = (it == _it) ? (NULL) : (*it);
}
h[i] = -1;
}
G.clear(), nxt.clear();
for (int i = n; i; i--) {
for (auto x : suf[i]) {
G.push_back(x);
nxt.push_back(h[x->r]);
h[x->r] = (signed) G.size() - 1;
}
}
for (int i = 1; i <= n; i++) {
auto st = pre[i].begin(), it = st;
auto _it = pre[i].end();
for (int _ = h[i]; ~_; _ = nxt[_]) {
auto& t = G[_];
while (it != _it && (*it)->l < t->l)
it++;
t->chl = (it == st) ? (NULL) : (*(it - 1));
}
h[i] = -1;
}
G.clear(), nxt.clear();
for (Status* p = pl; p != top; p++) {
int len = p->r - p->l;
G.push_back(p);
nxt.push_back(h[len]);
h[len] = (signed) G.size() - 1;
}
for (int _ = h[0]; ~_; _ = nxt[_]) {
auto& x = G[_];
x->f = -x->v;
}
for (int i = 0; i < n - 1; i++) {
for (int _ = h[i]; ~_; _ = nxt[_]) {
G[_]->update();
}
}
ll ans = llf;
for (int _ = h[n - 1]; ~_; _ = nxt[_]) {
ans = min(ans, G[_]->f);
}
for (int i = 1; i <= n; i++)
ans += a[i];
printf("%lld\n", ans);
return 0;
}
Problem D 新年的追逐战
不难注意到 $H$ 的连通块在 $G_i$ 是上是连通的。
考虑每个 $G_i$ 选一个连通块出来,那么在 $H$ 中会形成多少个连通块
- 如果存在一个连通块大小为 1,那么在 $H$ 中这些都是孤立点
- 如果其中有 $k$ 个是二分图,那么会有 $2^{\max\{k - 1, 0\}}$ 个连通块。证明的话,考虑对其中的二分图进行黑白染色,如果确定了其中一个图是在黑点还是在白点,剩下的二分图中是在黑点还是白点是确定的,充分性的话可以简单归纳一下。
相信数 $n$ 个点的带标号连通二分图大家都会。
剩下是个简单 dp。
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
#define ll long long
template <typename T>
void pfill(T* pst, const T* ped, T val) {
for ( ; pst != ped; *(pst++) = val);
}
template <typename T>
void pcopy(T* pst, const T* ped, T* pval) {
for ( ; pst != ped; *(pst++) = *(pval++));
}
const int N = 262144;
const int Mod = 998244353;
const int bzmax = 19;
const int g = 3;
void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
}
int inv(int a, int Mod) {
int x, y;
exgcd(a, Mod, x, y);
return (x < 0) ? (x + Mod) : (x);
}
template <const int Mod = :: Mod>
class Z {
public:
int v;
Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { }
friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~ (const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
};
typedef Z<> Zi;
Zi qpow(Zi a, int p) {
if (p < Mod - 1)
p += Mod - 1;
Zi rt = 1, pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
}
const Zi inv2 ((Mod + 1) >> 1);
class NTT {
private:
Zi gn[bzmax + 4], _gn[bzmax + 4];
public:
NTT() {
for (int i = 0; i <= bzmax; i++) {
gn[i] = qpow(Zi(g), (Mod - 1) >> i);
_gn[i] = qpow(Zi(g), -((Mod - 1) >> i));
}
}
void operator () (Zi* f, int len, int sgn) {
for (int i = 1, j = len >> 1, k; i < len - 1; i++, j += k) {
if (i < j)
swap(f[i], f[j]);
for (k = len >> 1; k <= j; j -= k, k >>= 1);
}
Zi *wn = (sgn > 0) ? (gn + 1) : (_gn + 1), w, a, b;
for (int l = 2, hl; l <= len; l <<= 1, wn++) {
hl = l >> 1, w = 1;
for (int i = 0; i < len; i += l, w = 1) {
for (int j = 0; j < hl; j++, w *= *wn) {
a = f[i + j], b = f[i + j + hl] * w;
f[i + j] = a + b;
f[i + j + hl] = a - b;
}
}
}
if (sgn < 0) {
Zi invlen = ~Zi(len);
for (int i = 0; i < len; i++) {
f[i] *= invlen;
}
}
}
int correct_len(int len) {
int m = 1;
for ( ; m <= len; m <<= 1);
return m;
}
} NTT;
void pol_inverse(Zi* f, Zi* g, int n) {
static Zi A[N];
if (n == 1) {
g[0] = ~f[0];
} else {
int hn = (n + 1) >> 1, t = NTT.correct_len(n << 1 | 1);
pol_inverse(f, g, hn);
pcopy(A, A + n, f);
pfill(A + n, A + t, Zi(0));
pfill(g + hn, g + t, Zi(0));
NTT(A, t, 1);
NTT(g, t, 1);
for (int i = 0; i < t; i++) {
g[i] = g[i] * (Zi(2) - g[i] * A[i]);
}
NTT(g, t, -1);
pfill(g + n, g + t, Zi(0));
}
}
void pol_sqrt(Zi* f, Zi* g, int n) {
static Zi A[N], B[N];
if (n == 1) {
g[0] = f[0];
} else {
int hn = (n + 1) >> 1, t = NTT.correct_len(n + n);
pol_sqrt(f, g, hn);
pfill(g + hn, g + n, Zi(0));
for (int i = 0; i < hn; i++)
A[i] = g[i] + g[i];
pfill(A + hn, A + t, Zi(0));
pol_inverse(A, B, n);
pcopy(A, A + n, f);
pfill(A + n, A + t, Zi(0));
NTT(A, t, 1);
NTT(B, t, 1);
for (int i = 0; i < t; i++)
A[i] *= B[i];
NTT(A, t, -1);
for (int i = 0; i < n; i++)
g[i] = g[i] * inv2 + A[i];
}
}
typedef class Poly : public vector<Zi> {
public:
using vector<Zi>::vector;
Poly& fix(int sz) {
resize(sz);
return *this;
}
} Poly;
Poly operator + (Poly A, Poly B) {
int n = A.size(), m = B.size();
int t = max(n, m);
A.resize(t), B.resize(t);
for (int i = 0; i < t; i++) {
A[i] += B[i];
}
return A;
}
Poly operator - (Poly A, Poly B) {
int n = A.size(), m = B.size();
int t = max(n, m);
A.resize(t), B.resize(t);
for (int i = 0; i < t; i++) {
A[i] -= B[i];
}
return A;
}
Poly sqrt(Poly a) {
Poly rt (a.size());
pol_sqrt(a.data(), rt.data(), a.size());
return rt;
}
Poly operator * (Poly A, Poly B) {
int n = A.size(), m = B.size();
int k = NTT.correct_len(n + m - 1);
if (n < 20 || m < 20) {
Poly rt (n + m - 1);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
rt[i + j] += A[i] * B[j];
}
}
return rt;
}
A.resize(k), B.resize(k);
NTT(A.data(), k, 1);
NTT(B.data(), k, 1);
for (int i = 0; i < k; i++) {
A[i] *= B[i];
}
NTT(A.data(), k, -1);
A.resize(n + m - 1);
return A;
}
Poly operator ~ (Poly f) {
int n = f.size(), t = NTT.correct_len((n << 1) | 1);
Poly rt (t);
f.resize(t);
pol_inverse(f.data(), rt.data(), n);
rt.resize(n);
return rt;
}
Poly operator / (Poly A, Poly B) {
int n = A.size(), m = B.size();
if (n < m) {
return Poly {0};
}
int r = n - m + 1;
reverse(A.begin(), A.end());
reverse(B.begin(), B.end());
A.resize(r), B.resize(r);
A = A * ~B;
A.resize(r);
reverse(A.begin(), A.end());
return A;
}
Poly operator % (Poly A, Poly B) {
int n = A.size(), m = B.size();
if (n < m) {
return A;
}
if (m == 1) {
return Poly {0};
}
A = A - A / B * B;
A.resize(m - 1);
return A;
}
Zi Inv[N];
void init_inv(int n) {
Inv[0] = 0, Inv[1] = 1;
for (int i = 2; i <= n; i++) {
Inv[i] = Inv[Mod % i] * Zi((Mod - (Mod / i)));
}
}
void diff(Poly& f) {
if (f.size() == 1) {
f[0] = 0;
return;
}
for (int i = 1; i < (signed) f.size(); i++) {
f[i - 1] = f[i] * Zi(i);
}
f.resize(f.size() - 1);
}
void integ(Poly& f) {
f.resize(f.size() + 1);
for (int i = (signed) f.size() - 1; i; i--) {
f[i] = f[i - 1] * Inv[i];
}
f[0] = 0;
}
Poly ln(Poly f) {
int n = f.size();
Poly h = f;
diff(h);
f = h * ~f;
f.resize(n - 1);
integ(f);
return f;
}
void pol_exp(Poly& f, Poly& g, int n) {
Poly h;
if (n == 1) {
g.resize(1);
g[0] = 1;
} else {
int hn = (n + 1) >> 1;
pol_exp(f, g, hn);
h.resize(n), g.resize(n);
pcopy(h.data(), h.data() + n, f.data());
g = g * (Poly{1} - ln(g) + h);
g.resize(n);
}
}
Poly exp(Poly f) {
int n = f.size();
Poly rt;
pol_exp(f, rt, n);
return rt;
}
class PolyBuilder {
protected:
int num;
Poly P[N << 1];
void _init(int *x, int l, int r) {
if (l == r) {
P[num++] = Poly{-Zi(x[l]), Zi(1)};
return;
}
int mid = (l + r) >> 1;
int curid = num++;
_init(x, l, mid);
int rid = num;
_init(x, mid + 1, r);
P[curid] = P[curid + 1] * P[rid];
}
void _evalute(Poly f, Zi* y, int l, int r) {
f = f % P[num++];
if (l == r) {
y[l] = f[0];
return;
}
int mid = (l + r) >> 1;
_evalute(f, y, l, mid);
_evalute(f, y, mid + 1, r);
}
public:
Poly evalute(Poly f, int* x, int n) {
Poly rt(n);
num = 0;
_init(x, 0, n - 1);
num = 0;
_evalute(f, rt.data(), 0, n - 1);
return rt;
}
} PolyBuilder;
int n, m;
int sz[N];
Zi fac[N], _fac[N];
void prepare(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i;
_fac[n] = ~fac[n];
for (int i = n; i; i--)
_fac[i - 1] = _fac[i] * i;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", sz + i);
m = max(m, sz[i]);
}
prepare(m);
init_inv(m);
Zi inv2 ((Mod + 1) >> 1);
Poly A (m + 1);
for (int i = 0; i <= m; i++) {
A[i] = qpow(inv2, 1ll * i * (i - 1) / 2 % (Mod - 1)) * _fac[i];
}
Poly biG = (A * A).fix(m + 1);
for (int i = 0; i <= m; i++) {
biG[i] = biG[i] * qpow(2, 1ll * i * (i - 1) / 2 % (Mod - 1));
}
Poly biF = ln(biG) * Poly {inv2};
Poly cmG (m + 1);
for (int i = 0; i <= m; i++)
cmG[i] = qpow(2, 1ll * i * (i - 1) / 2 % (Mod - 1)) * _fac[i];
Poly cmF = ln(cmG);
Poly cmFn = cmF;
for (int i = 0; i <= m; i++)
cmFn[i] *= i;
for (int i = 0; i <= m; i++)
A[i] = _fac[i] * qpow(2, 1ll * i * (i - 1) / 2 % (Mod - 1));
biF[1] = cmF[1] = 0;
Poly biH = (biF * A).fix(m + 1);
Poly cmH = (cmF * A).fix(m + 1);
Poly cmHn = (cmFn * A).fix(m + 1);
for (int i = 0; i <= m; i++) {
biH[i] *= fac[i];
cmH[i] *= fac[i];
cmHn[i] *= fac[i];
}
Zi f[2] = {1, 0}, g[2] = {1, 0};
for (int i = 1; i <= n; i++) {
int s = sz[i];
f[1] = f[1] * (biH[s] + cmH[s]) + f[0] * biH[s];
f[0] = f[0] * (cmH[s] - biH[s]);
Zi coef = s * qpow(2, 1ll * (s - 1) * (s - 2) / 2 % (Mod - 1));
g[1] = g[1] * cmHn[s] + g[0] * coef;
g[0] = g[0] * (cmHn[s] - coef);
}
Zi ans = f[1] + f[0] + g[1];
printf("%d\n", ans.v);
return 0;
}
Problem E 新年的邀请函
咕咕咕
Problem D(old) 新年的求值
考虑这样一个问题 $q_i = a^i$。
那么它的答案等于 $f(q_i) = \sum_{j = 0}^n f_j a^{ij}$
注意到 $ij = \binom{i + j}{2} - \binom{i}{2} - \binom{j}{2}$。所以可以做一次减法卷积。
原问题的话就用一些简单换元法可以把问题规约成上面。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
#define ll long long
template <typename T>
void pfill(T* pst, const T* ped, T val) {
for ( ; pst != ped; *(pst++) = val);
}
template <typename T>
void pcopy(T* pst, const T* ped, T* pval) {
for ( ; pst != ped; *(pst++) = *(pval++));
}
const int N = 1 << 21;
const int Mod = 998244353;
const int bzmax = 23;
const int g = 3;
void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
}
int inv(int a, int Mod) {
int x, y;
exgcd(a, Mod, x, y);
return (x < 0) ? (x + Mod) : (x);
}
template <const int Mod = :: Mod>
class Z {
public:
int v;
Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { }
friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~ (const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
};
typedef Z<> Zi;
Zi qpow(Zi a, int p) {
if (p < Mod - 1)
p += Mod - 1;
Zi rt = 1, pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
}
const Zi inv2 ((Mod + 1) >> 1);
class NTT {
private:
Zi gn[bzmax + 4], _gn[bzmax + 4];
public:
NTT() {
for (int i = 0; i <= bzmax; i++) {
gn[i] = qpow(Zi(g), (Mod - 1) >> i);
_gn[i] = qpow(Zi(g), -((Mod - 1) >> i));
}
}
void operator () (Zi* f, int len, int sgn) {
for (int i = 1, j = len >> 1, k; i < len - 1; i++, j += k) {
if (i < j)
swap(f[i], f[j]);
for (k = len >> 1; k <= j; j -= k, k >>= 1);
}
Zi *wn = (sgn > 0) ? (gn + 1) : (_gn + 1), w, a, b;
for (int l = 2, hl; l <= len; l <<= 1, wn++) {
hl = l >> 1, w = 1;
for (int i = 0; i < len; i += l, w = 1) {
for (int j = 0; j < hl; j++, w *= *wn) {
a = f[i + j], b = f[i + j + hl] * w;
f[i + j] = a + b;
f[i + j + hl] = a - b;
}
}
}
if (sgn < 0) {
Zi invlen = ~Zi(len);
for (int i = 0; i < len; i++) {
f[i] *= invlen;
}
}
}
int correct_len(int len) {
int m = 1;
for ( ; m <= len; m <<= 1);
return m;
}
} NTT;
typedef class Poly : public vector<Zi> {
public:
using vector<Zi>::vector;
Poly& fix(int sz) {
resize(sz);
return *this;
}
} Poly;
Poly operator * (Poly A, Poly B) {
int n = A.size(), m = B.size();
int k = NTT.correct_len(n + m - 1);
if (n < 20 || m < 20) {
Poly rt (n + m - 1);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
rt[i + j] += A[i] * B[j];
}
}
return rt;
}
A.resize(k), B.resize(k);
NTT(A.data(), k, 1);
NTT(B.data(), k, 1);
for (int i = 0; i < k; i++) {
A[i] *= B[i];
}
NTT(A.data(), k, -1);
A.resize(n + m - 1);
return A;
}
Zi fac[N], _fac[N];
void prepare(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i;
_fac[n] = ~fac[n];
for (int i = n; i; i--)
_fac[i - 1] = _fac[i] * i;
}
// f'(x) = f(x * d)
void mul(Poly& a, Zi x) {
Zi pw = 1;
for (auto& y : a)
y *= pw, pw *= x;
}
// f'(x) = f(x + d)
Poly poly_mov(Poly f, Zi d) {
int n = f.size();
Poly A (n);
for (int i = 0; i < n; i++)
f[i] *= fac[i];
Zi pw = 1;
for (int i = 0; i < n; i++, pw = pw * d)
A[i] = _fac[i] * pw;
reverse(A.begin(), A.end());
f = f * A;
for (int i = 0; i < n; i++)
f[i] = f[i + n - 1] * _fac[i];
f.resize(n);
return f;
}
int n, Q;
Poly f;
Zi q0, a, b;
int main() {
scanf("%d%d", &n, &Q);
f.resize(++n);
for (int i = 0; i < n; i++) {
scanf("%d", &f[i].v);
}
scanf("%d%d%d", &q0.v, &a.v, &b.v);
prepare(n + 1);
mul(f, ~Zi(a - 1));
f = poly_mov(f, -b);
mul(f, q0 * (a - 1) + b);
++Q;
Poly A (n + Q - 1);
A[0] = 1;
Zi pw = 1;
for (int i = 1; i < (signed) A.size(); i++) {
A[i] = A[i - 1] * pw;
pw *= a;
}
pw = 1;
Zi _a = ~a, pwa = 1;
for (int i = 1; i < n; i++)
f[i] *= pwa, pwa *= (pw *= _a);
reverse(f.begin(), f.end());
A = A * f;
unsigned ans = 0;
pw = pwa = 1;
for (int i = 1; i < Q; i++) {
Zi tmp = A[i + n - 1] * pwa;
pwa *= (pw *= _a);
ans ^= tmp.v;
}
printf("%u\n", ans);
return 0;
}
浙公网安备 33010602011771号