P15710 [JAG 2023 Summer Camp #2] Gemini Tree (Ver.Lapislazuli) 题解
首先容易发现树
易知
但是还要删叶子,所以要减去删了之后不是双子树的方案。
令
若
|D_k|\geqslant2 ,则D_k 中任取两相异元素C_1,C_2 ,C_1\setminus C_2 与C_2\setminus C_1 中均有叶子。 ::::info[证明] 显然k\ne0,k\ne n ,那么D_k 中元素都是通过断边生成的。不妨给T 随便定一个根,以边两端深度较大的点来指代边。记点x 子树点集为sub_x 。 记生成C_1,C_2 的这两条边对应点分别为x_1,x_2 。若x_1=x_2 ,C_1,C_2 必定一个是sub_{x_1} ,一个是T\setminus sub_{x_1} 子树,上述结论显然成立。对x_1,x_2 的关系分讨:
- 呈祖孙关系(
\operatorname{lca}(x_1,x_2)\in\{x_1,x_2\} )
不妨设
前者
-
不呈祖孙关系
此时要么
C_1=sub_{x_1},C_2=sub_{x_2} ,要么C_1=T\setminus sub_{x_1},C_2=T\setminus sub_{x_2} 。前者显然成立;后者
sub_{x_1} 中叶子都\in C_2\setminus C_1 ,sub_{x_2} 中叶子都\in C_1\setminus C_2 。 ::::
所以如果
现在只用管
将叶子按 dfs 序排序,则每个连通块所包含的叶子形如 lower_bound。
::::success[Code]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
namespace IO {
constexpr int bufsize = 230005;
char buf[bufsize], *f1, *f2;
char gtchar() {return f1 == f2 && (f2 = buf + fread(f1 = buf, 1, bufsize, stdin)) == buf? EOF: *f1++;}
template<typename T> void read(T &ret)
{
int f = ret = 0;
char ch = gtchar();
while(!isdigit(ch)) f = ch == '-', ch = gtchar();
while(isdigit(ch)) ret = (ret << 3) + (ret << 1) + (ch ^ 48), ch = gtchar();
if(f) ret = -ret;
}
template<typename T, typename ...t> void read(T &a, t &...b) {read(a), read(b...);}
}using IO::read;
template<typename t, typename T = t> void chmax(t &a, const T &b) {if(a < b) a = b;}
template<typename t, typename T = t> void chmin(t &a, const T &b) {if(a > b) a = b;}
constexpr int maxn = 1e5 + 5, mod = 998244353;
int Add(int a, const int &b) {return (a += b) >= mod? a - mod: a;}
int n, cnt, dfn[maxn], sz[maxn];
ll fac[maxn], inv[maxn];
vector<int> g[maxn], leaf;
struct range {
int l, r, mode;//0:[l,r] 1:[0,l),(r,leaf.size()-1]
int size() {return mode? leaf.size() - (r - l + 1): r - l + 1;}
};
vector<range> D[maxn];
void dfs(int k, int pre)
{
sz[k] = 1, dfn[k] = ++cnt;
if(g[k].size() == 1) leaf.push_back(dfn[k]);
for(int i : g[k]) if(i != pre) dfs(i, k), sz[k] += sz[i];
}
void dfs2(int k, int pre)
{
int l = lower_bound(leaf.begin(), leaf.end(), dfn[k]) - leaf.begin();
int r = lower_bound(leaf.begin(), leaf.end(), dfn[k] + sz[k]) - leaf.begin() - 1;
// printf("%d:%d,%d\n", k, l, r);
D[n - sz[k]].push_back({l, r, 1});
D[sz[k]].push_back({l, r});
for(int i : g[k]) if(i != pre) dfs2(i, k);
}
int binom(int a, int b) {return a >= b && b >= 0? fac[a] * inv[b] % mod * inv[a - b] % mod: 0;}
bool inter(range a, range b)
{
if(a.mode && b.mode) return !(min(a.l, b.l) == 0 && max(a.r, b.r) == leaf.size() - 1 && max(a.l, b.l) - 1 <= min(a.r, b.r));
if(b.mode) swap(a, b);
if(a.mode) return !(a.l <= b.l && b.r <= a.r);
return max(a.l, b.l) <= min(a.r, b.r);
}
bool contain(range a, range b)
{
if(a.mode && b.mode) return b.l <= a.l && a.r <= b.r;
if(a.mode) return b.r < a.l || b.l > a.r;
if(b.mode) return a.l == 0 && a.r == leaf.size() - 1;
return a.l <= b.l && b.r <= a.r;
}
int main()
{
read(n);
inv[0] = inv[1] = fac[0] = fac[1] = 1;
for(int i = 2; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
for(int i = 2; i <= n; i++) inv[i] = mod - mod / i * inv[mod % i] % mod;
for(int i = 2; i <= n; i++) inv[i] = inv[i - 1] * inv[i] % mod;
for(int i = 1, x, y; i < n; i++)
{
read(x, y);
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1, 0);
dfs2(1, 0);
int ans = 0;
for(int i = 0; i <= n; i++) if(D[i].size()) ans = Add(ans, binom(n, i));//, printf("i(%d)\n", i);
// printf("%d\n", ans);
for(int i = 1; i < n; i++) if(D[i].size() == 1)
{
auto now = D[i][0];
// printf("now:%d %d %d\n", now.l, now.r, now.mode);
int tmp = binom(n - leaf.size(), i - int(leaf.size() - now.size()));
int f = 1;
for(auto ele : D[i + 1]) if(inter(ele, now)) {f = 0; break;}
now.mode ^= 1;
if(f) for(auto ele : D[i - 1]) if(!contain(ele, now)) {f = 0; break;}
if(f) ans = Add(ans, mod - tmp);//, printf("ban %d(%d)\n", tmp, i);
}
printf("%d\n", ans);
return 0;
}
::::