20250805
T1
图的计数
考虑 \(dp_{i, j}\) 表示凑了 \(i\),最后一个用的 \(j\) 的方案数。根号分治,\(j\) 只跑到根号。剩下的画图,发现把剩下的扣掉根号之后换个方向看就又变成刚才求的东西了。于是就做完了。
代码
#include <iostream>
#include <string.h>
#define int long long
using namespace std;
const int P = 999999599;
const int B = 500;
int qpow(int x, int y = P - 2) {
int ret = 1;
while (y) {
if (y & 1)
ret = ret * x % P;
y >>= 1;
x = x * x % P;
}
return ret;
}
int n, m, X;
int f[505][505];
int v1[200005], v2[200005];
signed main() {
freopen("graph.in", "r", stdin);
freopen("graph.out", "w", stdout);
cin >> n >> m;
X = max(n / B, B) + 1;
for (int i = 0; i <= X; i++) {
f[0][i] = 1, v1[0] = 1;
if (i * B <= n)
v2[n - i * B]++;
}
for (int i = 1; i <= n; i++) {
int c = i % X;
memset(f[c], 0, sizeof f[c]);
for (int j = 1; j <= min(max(n / B, B - 1), i); j++) f[c][j] = f[(i - j % X + X) % X][j];
for (int j = 1; j < max(n / B + 1, B); j++) f[c][j] = (f[c][j] + f[c][j - 1]) % (P - 1);
for (int j = 0; j <= X; j++) {
if (i + j * B <= n)
v2[n - (i + j * B)] = (v2[n - (i + j * B)] + f[c][j]) % (P - 1);
}
v1[i] = f[c][B - 1];
}
int ans = 0;
for (int i = 0; i <= n; i++) ans += (1ll * v1[i]) * v2[i] % (P - 1);
cout << qpow(m, ans % (P - 1)) << "\n";
return 0;
}
T2
祖先
把贡献拆开,变成自己和子树,轻子树和重子树,轻子树之间。重剖,每次修改跳重链更新链顶父亲的信息。若修改在子树中,这样就 ok。若子树加在祖先,考虑把这些操作叠一块做,总的效果相当于自己子树加 \(x\)。拆贡献,发现只需要维护每个轻子树点贡献次数之和、轻子树互相贡献的对(pair)数、轻子树点权和即可实现更新答案。另外还需要各轻子树自己的点权和。这都是好维护的。
代码
#include <iostream>
#define int long long
#define lowbit(x) ((x) & (-(x)))
using namespace std;
int n, q;
struct BIT {
unsigned int bit[200005];
void add(int x, unsigned int y) { for (; x <= n; x += lowbit(x)) bit[x] += y; }
unsigned int query(int x) {
unsigned int ret = 0;
for (; x; x -= lowbit(x)) ret += bit[x];
return ret;
}
} bit;
unsigned int a[200005];
int head[200005], nxt[200005], to[200005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int _dfn[200005], top[200005], dfn[200005], f[200005], ncnt;
int *L = dfn, R[200005];
int son[200005];
unsigned int sz[200005];
unsigned int sh[200005], sl[200005], ctl[200005], sv[200005], cnt[200005];
unsigned int val[200005];
struct Segment_Tree {
unsigned int s[800005], tg[800005], len[800005];
void tag(int o, unsigned int v) { s[o] += v * len[o], tg[o] += v; }
void pushdown(int o) {
if (!tg[o])
return;
tag(o << 1, tg[o]);
tag(o << 1 | 1, tg[o]);
tg[o] = 0;
}
void Build(int o, int l, int r) {
len[o] = r - l + 1;
if (l == r) {
s[o] = a[_dfn[l]];
return;
}
int mid = (l + r) >> 1;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
s[o] = s[o << 1] + s[o << 1 | 1];
}
void Add(int o, int l, int r, int L, int R, unsigned int v) {
if (L <= l && r <= R)
return tag(o, v);
pushdown(o);
int mid = (l + r) >> 1;
if (L <= mid)
Add(o << 1, l, mid, L, R, v);
if (R > mid)
Add(o << 1 | 1, mid + 1, r, L, R, v);
s[o] = s[o << 1] + s[o << 1 | 1];
}
unsigned int Query(int o, int l, int r, int L, int R) {
if (L > R)
return 0;
if (L <= l && r <= R)
return s[o];
pushdown(o);
int mid = (l + r) >> 1;
if (R <= mid)
return Query(o << 1, l, mid, L, R);
if (L > mid)
return Query(o << 1 | 1, mid + 1, r, L, R);
return Query(o << 1, l, mid, L, R) + Query(o << 1 | 1, mid + 1, r, L, R);
}
} seg;
void dfs1(int x) {
sz[x] = 1;
sv[x] = a[x];
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
dfs1(v);
sv[x] += sv[v];
sz[x] += sz[v];
if (sz[v] > sz[son[x]])
son[x] = v;
}
unsigned int c = 0;
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if (v != son[x]) {
val[x] += sv[v] * (sz[x] - sz[son[x]] - sz[v] - 1);
cnt[x] += sz[v] * c;
c += sz[v];
}
}
}
void dfs2(int x, int t) {
_dfn[dfn[x] = ++ncnt] = x;
top[x] = t;
if (son[x])
dfs2(son[x], t);
sh[x] = sv[son[x]];
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if (v != son[x]) {
dfs2(v, v);
ctl[x] += sv[v] * sl[x];
sl[x] += sv[v];
}
}
R[x] = ncnt;
}
void Change(int x, unsigned int y) {
while (f[top[x]]) {
x = top[x];
ctl[f[x]] += (sl[f[x]] - sv[x]) * y;
sl[f[x]] += y;
val[f[x]] += y * (sz[f[x]] - sz[x] - sz[son[f[x]]] - 1);
sv[x] += y;
x = f[x];
}
}
unsigned int Query(int x) {
unsigned int vx = seg.Query(1, 1, n, dfn[x], dfn[x]), tg = bit.query(dfn[x]);
unsigned int sumh = 0, suml = 0;
if (son[x])
sumh = seg.Query(1, 1, n, L[son[x]], R[son[x]]), suml = seg.Query(1, 1, n, R[son[x]] + 1, R[x]);
unsigned int v1 = vx * (sumh + suml);
unsigned int v2 = sumh * suml;
unsigned int v3 = ctl[x] + tg * val[x] + tg * tg * cnt[x];
return v1 + v2 + v3;
}
signed main() {
freopen("ancestor.in", "r", stdin);
freopen("ancestor.out", "w", stdout);
cin >> n >> q;
for (int i = 2; i <= n; i++) cin >> f[i], add(f[i], i);
for (int i = 1; i <= n; i++) cin >> a[i];
dfs1(1);
dfs2(1, 1);
seg.Build(1, 1, n);
while (q--) {
char op;
int x;
unsigned int y;
cin >> op >> x;
if (op == 'S') {
cin >> y;
seg.Add(1, 1, n, dfn[x], dfn[x], y);
Change(x, y);
} else if (op == 'M') {
cin >> y;
Change(x, y * sz[x]);
bit.add(L[x], y);
bit.add(R[x] + 1, -y);
seg.Add(1, 1, n, L[x], R[x], y);
} else
cout << Query(x) % (1ull << 63) << "\n";
}
return 0;
}
T3
大哭
首先若序列确定,我们一定按做菜时间从慢到快做。因此按这个顺序 dp。接下来的 dp 没完全懂。大概就是首先你考虑若两个 \(a_i\) 构成逆序对,那一定不会选后面的扔掉前面的,因为这样不优。因此必然存在一条左下到右上的折线满足其上方的点都被扔掉,下方的都被保留。我们先考虑枚举答案,求出有多少序列的答案比这个大。由于对于每个序列来说,不合法的时间数量就是其最短时间,因此求出这个东西之后答案是容易算的。那么接下来考虑对着折线 dp。我们需要让每一个 \(a\) 序列唯一对应一条折线。显然的考虑是让折线尽可能靠下,也就是卡着保留的东西。然后折线上同一横之间的元素我们钦定前面的被扔掉,后面的保留。然后考虑我们扔掉一些东西,肯定是因为留下它们会爆掉当前枚举的答案。也就是我们在每个位置检查,若这个位置的前缀和加当前 \(b_i\) 加当前折线位置爆了,那么说明我们这一段扔掉的东西是合理的,接下来可以选择把折线往上移,或者留在原位置(如果当前这一横着的段没有保留过东西)。那么最后我们希望所有折线扔掉的东西都被验证合理,于是让所有折线最后都走到 \(m + 1\) 高度。于是有 dp:\(f_{i, j, k, l, 0 / 1/ 2}\) 表示考虑了前 \(i\) 个,保留的东西的和为 \(j\),保留了 \(k\) 个东西,当前折线在 \(l\),(没见过 \(l\),见过 \(l\) 且都扔了,见过 \(l\) 且扔了且后面留了一段后缀),的方案数。转移枚举新填个啥即可。容易用前缀和优化。
代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
const int P = 998244353, N = 700;
inline void Madd(int &x, int y) { (x += y) >= P ? (x -= P) : 0; }
int n, m;
int b[505];
int f[35][705][35][25][3];
// at i, presum j, choose k, line pos l
int g[705][35][25][3];
int ans[35], mxb;
signed main() {
freopen("dk.in", "r", stdin);
freopen("dk.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> b[i], mxb = max(mxb, b[i]);
sort(b + 1, b + n + 1, greater<int>());
for (int j = 0; j <= N; j++) for (int i = 1; i <= m + 1; i++) f[0][j][0][i][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= N; j++) for (int k = 0; k <= i; k++) for (int l = 1; l <= m + 1; l++) f[i][j][k][l][0] = f[i][j][k][l][1] = f[i][j][k][l][2] = 0;
memset(g, 0, sizeof g);
for (int j = 0; j <= N; j++) {
for (int k = 0; k < i; k++) {
for (int l = 1; l <= m + 1; l++) {
// choose < l
Madd(g[max(0, j - l + 1)][k + 1][l][0], f[i - 1][j][k][l][0]);
Madd(g[max(0, j - l + 1)][k + 1][l][1], f[i - 1][j][k][l][1]);
Madd(g[max(0, j - l + 1)][k + 1][l][2], f[i - 1][j][k][l][2]);
Madd(g[j][k + 1][l][0], P - f[i - 1][j][k][l][0]);
Madd(g[j][k + 1][l][1], P - f[i - 1][j][k][l][1]);
Madd(g[j][k + 1][l][2], P - f[i - 1][j][k][l][2]);
// choose = l
if (l != m + 1) {
Madd(f[i][j][k][l][1], f[i - 1][j][k][l][0]);
Madd(f[i][j][k][l][1], f[i - 1][j][k][l][1]);
if (l <= j) {
Madd(f[i][j - l][k + 1][l][2], f[i - 1][j][k][l][1]);
Madd(f[i][j - l][k + 1][l][2], f[i - 1][j][k][l][2]);
}
}
// choose > l
if (l != m + 1) {
Madd(f[i][j][k][l][0], 1ll * f[i - 1][j][k][l][0] * (m - l) % P);
Madd(f[i][j][k][l][1], 1ll * f[i - 1][j][k][l][1] * (m - l) % P);
Madd(f[i][j][k][l][2], 1ll * f[i - 1][j][k][l][2] * (m - l) % P);
}
}
}
}
for (int j = 0; j <= N; j++) for (int k = 0; k <= i; k++) for (int l = 1; l <= m + 1; l++) {
if (j) {
Madd(g[j][k][l][0], g[j - 1][k][l][0]);
Madd(g[j][k][l][1], g[j - 1][k][l][1]);
Madd(g[j][k][l][2], g[j - 1][k][l][2]);
}
Madd(f[i][j][k][l][0], g[j][k][l][0]);
Madd(f[i][j][k][l][1], g[j][k][l][1]);
Madd(f[i][j][k][l][2], g[j][k][l][2]);
}
memset(g, 0, sizeof g);
for (int j = 0; j <= N; j++) {
for (int k = 0; k <= i; k++) {
for (int l = 1; l <= m + 1; l++) {
if (b[i] > j)
f[i][j][k][l][0] = f[i][j][k][l][1] = f[i][j][k][l][2] = 0;
else if (b[i] + l > j) {
Madd(g[j][k][l][0], f[i][j][k][l][1]);
Madd(g[j][k][l + 1][0], f[i][j][k][l][2]);
f[i][j][k][l][1] = f[i][j][k][l][2] = 0;
}
}
}
}
for (int j = 0; j <= N; j++) for (int k = 0; k <= i; k++) for (int l = 1; l <= m + 1; l++) Madd(g[j][k][l][0], g[j][k][l - 1][0]), Madd(f[i][j][k][l][0], g[j][k][l][0]);
}
for (int j = 0; j <= N; j++) {
for (int k = n; k; k--) {
Madd(f[n][j][k][m + 1][0], f[n][j][k + 1][m + 1][0]);
Madd(ans[k], f[n][j][k][m + 1][0]);
}
}
long long pw = 1;
for (int i = 1; i <= n; i++) pw = pw * m % P;
for (int i = 1; i <= n; i++) cout << ((N + 1) * pw + P - ans[i]) % P << " ";
cout << "\n";
return 0;
}
T4
树
第一问答案为奇度点个数除以二上取整。接下来考虑第二问。
先二分答案,设 \(f_i\) 表示子树链最少且合法情况下最短向父亲延伸长度为多少的链。若当前点儿子有奇数个,那么一定要延伸上去一条,枚举延伸哪条上去,把剩下的大配小,检查合法性即可。接下来若当前点有偶数个儿子,那么先检查子树两两配对,若不能则我们一定扔掉最长的儿子,剩下的做奇数情况即可。
接下来考虑第三问。我们设 \(f_{i, j}\) 表示 \(i\) 子树向上延伸 \(j\) 长度的方案数。然后还是考虑当前点儿子数奇偶性。若有奇数个儿子,还是枚举哪条延伸上去,接下来只需要求剩下的儿子两两配对,链长不超过第二问答案的合法方案数。可以先求出 \(c_{i, j}\) 表示 \(i\) 和 \(j\) 儿子这样配对的方案数,然后只需要一个状压 dp 即可。每次把当前状态的 lowbit 拿出来找匹配,这样做的话这个状压 dp 的合法状态数就会很少。\(c_{i, j}\) 可以枚举一个儿子,然后在另一个儿子的 dp 数组上前缀和一下。然后考虑偶数。先算子树两两万门匹配的方案数,然后若当前点不为根,考虑枚举一个儿子向上延伸。再枚举扔掉的儿子,则方案数也是容易计算的。这样就是 \(n^2\) 的。发现状态和深度有关,考虑长剖。转移时特殊考虑长儿子,只需要拿数据结构维护区间乘单点加区间求和即可。实现上可以考虑对着 dfn 开线段树,dp 数组地址按 dfn 分配。总复杂度 \(\mathcal{O}(能过)\)。
代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
const int P = 998244353;
inline void Madd(int &x, int y) { (x += y) >= P ? (x -= P) : 0; }
inline int Msum(int x, int y) { return Madd(x, y), x; }
#define getchar() p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++
char buf[1<<21], *p1, *p2, ch;
long long read() {
long long ret = 0, neg = 0; char c = getchar(); neg = (c == '-');
while (c < '0' || c > '9') c = getchar(), neg |= (c == '-');
while (c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar();
return ret * (neg ? -1 : 1);
}
int n, type, cod, ans2;
int deg[100005], f[100005];
vector<int> G[100005];
namespace Type_2 {
int mid, ok;
void dfs0(int x, int fa) {
f[x] = fa;
if (x != 1)
G[x].erase(find(G[x].begin(), G[x].end(), fa));
for (auto v : G[x]) dfs0(v, x);
}
int dfs(int x) {
vector<int> g;
for (int v : G[x]) {
g.emplace_back(dfs(v));
ok &= (g.back() <= mid);
}
sort(g.begin(), g.end());
int scnt = G[x].size();
if (!(scnt & 1)) {
bool no = 0;
for (int i = 0, j = scnt - 1; i < j; i++, j--) no |= (g[i] + g[j] > mid);
if (!no)
return 1;
if (x == 1) {
ok = 0;
return 0;
}
g.pop_back();
}
for (int i = 0; i < (int)g.size(); i++) {
bool no = 0;
for (int j = (!i), k = (int)g.size() - 1 - (i == (int)g.size() - 1); j < k; j++, j += (j == i), k--, k -= (k == i))
no |= (g[j] + g[k] > mid);
if (!no)
return g[i] + 1;
}
ok = 0;
return 0;
}
int work() {
dfs0(1, 0);
int l = 1, r = n, ans = 0;
while (l <= r) {
mid = (l + r) >> 1;
if (ok = 1, dfs(1), ok)
ans = mid, r = mid - 1;
else
l = mid + 1;
}
return ans;
}
}
namespace Type_3 {
struct Segment_Tree {
int s[400005], mtag[400005];
inline void tag(int o, int v) { mtag[o] = 1ll * mtag[o] * v % P, s[o] = 1ll * s[o] * v % P; }
inline void pushdown(int o) {
if (mtag[o] == 1)
return;
tag(o << 1, mtag[o]);
tag(o << 1 | 1, mtag[o]);
mtag[o] = 1;
}
void Build(int o, int l, int r) {
mtag[o] = 1;
if (l == r)
return;
int mid = (l + r) >> 1;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
}
void Add(int o, int l, int r, int x, int y) {
if (l == r)
return Madd(s[o], y);
pushdown(o);
int mid = (l + r) >> 1;
if (x <= mid)
Add(o << 1, l, mid, x, y);
else
Add(o << 1 | 1, mid + 1, r, x, y);
s[o] = Msum(s[o << 1], s[o << 1 | 1]);
}
void Mul(int o, int l, int r, int L, int R, int v) {
if (L <= l && r <= R)
return tag(o, v);
pushdown(o);
int mid = (l + r) >> 1;
if (L <= mid)
Mul(o << 1, l, mid, L, R, v);
if (R > mid)
Mul(o << 1 | 1, mid + 1, r, L, R, v);
s[o] = Msum(s[o << 1], s[o << 1 | 1]);
}
int Query(int o, int l, int r, int L, int R) {
if (L <= l && r <= R)
return s[o];
pushdown(o);
int mid = (l + r) >> 1;
if (R <= mid)
return Query(o << 1, l, mid, L, R);
if (L > mid)
return Query(o << 1 | 1, mid + 1, r, L, R);
return Msum(Query(o << 1, l, mid, L, R), Query(o << 1 | 1, mid + 1, r, L, R));
}
} seg;
vector<int> bit[70005];
int dist[100005], son[100005], dfn[100005], ncnt;
int vis[70005], rec[70005], val[20][20], X;
int dp[100005];
int dfs(int S) {
if (vis[S] == X)
return rec[S];
if (!S)
return 1;
vis[S] = X, rec[S] = 0;
for (int i = 1; i < (int)bit[S].size(); i++) {
int a = bit[S][0], b = bit[S][i];
Madd(rec[S], 1ll * dfs(S ^ (1 << a) ^ (1 << b)) * val[a][b] % P);
}
return rec[S];
}
void dfs1(int x) {
for (int v : G[x]) {
dfs1(v);
dist[x] = max(dist[x], dist[v] + 1);
if (dist[v] >= dist[son[x]])
son[x] = v;
}
}
vector<int> tmp[20];
void dfs2(int x) {
dfn[x] = ++ncnt;
if (!dist[x]) {
seg.Add(1, 1, n, dfn[x], 1);
dp[dfn[x]] = 1;
return;
}
dfs2(son[x]);
for (int v : G[x]) v != son[x] ? dfs2(v) : void();
int scnt = G[x].size(), S = (1 << scnt) - 1, hid = 0;
memset(val, 0, sizeof val);
for (int i = 0; i < scnt; i++) {
int v = G[x][i]; tmp[i].resize(0);
if (v == son[x]) {
hid = i;
continue;
}
tmp[i].resize(dist[v] + 2);
for (int j = 1; j < dist[v] + 2; j++) tmp[i][j] = Msum(tmp[i][j - 1], dp[dfn[v] + j - 1]);
}
for (int i = 0; i < scnt; i++) {
int v1 = G[x][i], v2;
for (int j = i + 1; j < scnt; j++) {
v2 = G[x][j];
if (v1 == son[x] || v2 == son[x])
continue;
int a, b = min(ans2 - 1, dist[v2] + 1);
for (a = 1; a <= dist[v1] + 1; a++) {
b -= (a + b > ans2);
if (b <= 0)
break;
Madd(val[i][j], 1ll * dp[dfn[v1] + a - 1] * tmp[j][b] % P);
Madd(val[j][i], 1ll * dp[dfn[v1] + a - 1] * tmp[j][b] % P);
}
} v2 = son[x];
if (v1 == son[x])
continue;
int a, b = min(ans2 - 1, dist[v2] + 1);
for (a = 1; a <= dist[v1] + 1; a++) {
b -= (a + b > ans2);
if (b <= 0)
break;
Madd(val[i][hid], 1ll * dp[dfn[v1] + a - 1] * seg.Query(1, 1, n, dfn[v2], dfn[v2] + b - 1) % P);
Madd(val[hid][i], 1ll * dp[dfn[v1] + a - 1] * seg.Query(1, 1, n, dfn[v2], dfn[v2] + b - 1) % P);
}
}
X = x;
if (scnt & 1) {
seg.Mul(1, 1, n, dfn[son[x]], dfn[x] + dist[x], dfs(S ^ (1 << hid)));
for (int i = 0; i < scnt; i++) {
int v = G[x][i], coe;
if (v != son[x]) {
coe = dfs(S ^ (1 << i));
for (int j = 1; j <= dist[v] + 1; j++) seg.Add(1, 1, n, dfn[x] + j, 1ll * dp[dfn[v] + j - 1] * coe % P);
}
}
} else {
if (x != 1 || dfs(S) == 0) {
for (int i = 0; i < scnt; i++) {
int v = G[x][i];
if (i == hid) {
tmp[i].resize(1);
tmp[i][0] = seg.Query(1, 1, n, dfn[v], dfn[v] + min(ans2, dist[v] + 1) - 1);
} else
tmp[i][0] = tmp[i][min(ans2, dist[v] + 1)];
}
int s = 0;
for (int j = 0; j < scnt; j++) {
if (j != hid)
Madd(s, 1ll * tmp[j][0] * dfs(S ^ (1 << hid) ^ (1 << j)) % P);
}
seg.Mul(1, 1, n, dfn[son[x]], dfn[x] + dist[x], s);
for (int i = 0; i < scnt; i++) {
int v = G[x][i];
if (v != son[x]) {
for (int j = 0; j < scnt; j++) {
if (i != j) {
for (int k = 1; k <= min(dist[v] + 1, ans2 - 1); k++)
seg.Add(1, 1, n, dfn[x] + k, 1ll * dp[dfn[v] + k - 1] * tmp[j][0] % P * dfs(S ^ (1 << i) ^ (1 << j)) % P);
}
}
}
}
} else
seg.Mul(1, 1, n, dfn[x] + 1, dfn[x] + dist[x], 0);
seg.Add(1, 1, n, dfn[x], dfs(S));
}
if (son[f[x]] != x) {
for (int i = 1; i <= dist[x] + 1; i++)
dp[dfn[x] + i - 1] = seg.Query(1, 1, n, dfn[x] + i - 1, dfn[x] + i - 1);
}
}
int work() {
for (int i = 0; i < (1 << 16); i++) {
for (int j = 0; j < 16; j++) {
if (i & (1 << j))
bit[i].emplace_back(j);
}
}
seg.Build(1, 1, n);
dfs1(1);
dfs2(1);
return seg.Query(1, 1, n, 1, min(dist[1] + 1, ans2 + 1));
}
}
signed main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
n = read(), type = read();
for (int i = 1; i < n; i++) {
int u = read(), v = read();
++deg[u], ++deg[v];
G[u].emplace_back(v);
G[v].emplace_back(u);
}
for (int i = 1; i <= n; i++) cod += (deg[i] & 1);
cout << (cod + 1) / 2 << "\n";
if (type == 1)
return 0;
ans2 = Type_2::work();
cout << ans2 << "\n";
if (type == 2)
return 0;
cout << Type_3::work() << "\n";
return 0;
}
根号求分拆数。