題庫連接php
給你一棵區間爲 \([1,n]\) 的線段樹,求這棵樹的最大匹配以及對應的方案數,取模。c++
\(1\leq n\leq 10^{18}\)spa
記 \(f_{n,1/0}\) 表示區間長度爲 \(n\) 時,所對應的子樹根節點選或不選時的最大匹配,\(g_{n,1/0}\) 表示取對應的最大值時的方案數。code
容易發現長度爲 \(n\) 的狀態只會由 \(\left\lfloor\frac{n}{2}\right\rfloor\) 和 \(n-\left\lfloor\frac{n}{2}\right\rfloor\) 轉移過來。ip
那麼直接開 \(\text{map}\) 存便可,狀態數是 \(O(\log(n))\) 的。get
#include <bits/stdc++.h> #define ll long long #define CAL(x) cal(f[0][x], f[1][x], g[0][x], g[1][x]) using namespace std; const int yzh = 998244353; map<ll, ll> f[2]; map<ll, int> g[2]; ll n; int cal(ll a, ll b, int c, int d) { if (a == b) return (c+d)%yzh; return a > b ? c : d; } void dp(ll n) { if (g[0].count(n)) return; ll lx, ly; dp(lx = n/2); dp(ly = n-lx); f[0][n] = max(f[0][lx], f[1][lx])+max(f[0][ly], f[1][ly]); g[0][n] = 1ll*CAL(lx)*CAL(ly)%yzh; if (f[0][lx]+max(f[0][ly], f[1][ly]) >= f[0][ly]+max(f[0][lx], f[1][lx])) f[1][n] = f[0][lx]+max(f[0][ly], f[1][ly])+1, (g[1][n] += 1ll*g[0][lx]*CAL(ly)%yzh) %= yzh; if (f[0][lx]+max(f[0][ly], f[1][ly]) <= f[0][ly]+max(f[0][lx], f[1][lx])) f[1][n] = f[0][ly]+max(f[0][lx], f[1][lx])+1, (g[1][n] += 1ll*g[0][ly]*CAL(lx)%yzh) %= yzh; } int main() { scanf("%lld", &n); g[0][1] = 1, g[1][1] = 0; dp(n); if (f[0][n] == f[1][n]) printf("%lld %d\n", f[0][n], (g[0][n]+g[1][n])%yzh); else if (f[0][n] > f[1][n]) printf("%lld %d\n", f[0][n], g[0][n]); else printf("%lld %d\n", f[1][n], g[1][n]); return 0; }