[BZOJ 5123][Lydsy1712月赛]线段树的匹配
Description
给你一棵区间为 $[1,n]$ 的线段树,求这棵树的最大匹配以及对应的方案数,取模。
$1\leq n\leq 10^{18}$
Solution
记 $f_{n,1/0}$ 表示区间长度为 $n$ 时,所对应的子树根节点选或不选时的最大匹配,$g_{n,1/0}$ 表示取对应的最大值时的方案数。
容易发现长度为 $n$ 的状态只会由 $\left\lfloor\frac{n}{2}\right\rfloor$ 和 $n-\left\lfloor\frac{n}{2}\right\rfloor$ 转移过来。
那么直接开 $\text{map}$ 存即可,状态数是 $O(\log(n))$ 的。
Code
#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;
}
博主蒟蒻,随意转载。但必须附上原文链接:http://www.cnblogs.com/NaVi-Awson/,否则你会终生找不到妹子!!!