题解:P16905 [CCO 2026] Tree Traversals
第二条限制等价于按深度分层,层间顺序不变,只考虑层内顺序的贡献。对于同一层,记录
分类讨论。
注意到
因此把层内点按 dfs 序排序,二分得出在
预处理上述贡献,
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int n , q;
int dep[500010] , fa[500010][21];
struct edge {int to , nxt;}e[1000010];
int head[500010] , tot;
void add(int u , int v)
{
++ tot;
e[tot].to = v;
e[tot].nxt = head[u];
head[u] = tot;
}
int lca(int u , int v)
{
if(dep[u] < dep[v]) swap(u , v);
int d = dep[u] - dep[v];
for(int i = 0 ; i <= 20 ; i ++)
{
if(d & 1) u = fa[u][i];
d >>= 1;
}
if(u == v) return v;
for(int i = 20 ; i >= 0 ; i --)
{
if(fa[u][i] == fa[v][i]) continue;
u = fa[u][i] , v = fa[v][i];
}
return fa[u][0];
}
int cnt = 0 , dfn[500010];
int mxdep[500010] , sz[500010];
void dfs(int u , int pa)
{
fa[u][0] = pa , mxdep[u] = dep[u] = dep[pa] + 1 , dfn[u] = ++ cnt , sz[u] = 1;
for(int i = 1 ; i <= 20 ; i ++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(int i = head[u] ; i != 0 ; i = e[i].nxt)
{
int v = e[i].to;
if(v == pa) continue;
dfs(v , u);
mxdep[u] = max(mxdep[u] , mxdep[v]);
sz[u] += sz[v];
}
}
int Dis(int u , int v)
{
int LCA = lca(u , v);
return dep[u] + dep[v] - 2 * dep[LCA];
}
int dis[500010] , LCA[500010];
vector<int> vec[500010] , V[500010];
int fac[500010];
void solve()
{
scanf("%lld%lld" , &n , &q);
for(int i = 1 , u , v ; i < n ; i ++) scanf("%lld%lld" , &u , &v) , add(u , v) , add(v , u);
dfs(1 , 0);
for(int i = 1 ; i <= n ; i ++) vec[dep[i]].push_back(i) , V[dep[i]].push_back(dfn[i]);
for(int i = 1 ; i <= n ; i ++) sort(V[i].begin() , V[i].end());
int mx = 0;
for(int i = 1 ; i <= n ; i ++)
{
if(vec[i].size() == 0) break;
int L = vec[i][0];
for(int _ = 1 ; _ < (int)vec[i].size() ; _ ++) L = lca(L , vec[i][_]);
dis[i] = 2 * (i - dep[L]) , LCA[i] = L;
mx = max(mx , dis[i]);
}
int ans = 1 , res = 1;
// printf("mx:%lld\n" , mx);
for(int i = 1 ; i <= n ; i ++)
{
if(vec[i].size() == 0) break;
if(mx == dis[i] && vec[i + 1].size() != 0)
{
int p = LCA[i] , len = 0;
// printf("i:%lld p:%lld\n" , i , p);
for(int j = head[p] ; j != 0 ; j = e[j].nxt)
{
int v = e[j].to;
if(v == fa[p][0]) continue;
if(mxdep[v] <= i) continue;
// printf("v:%lld mxdep[v]:%lld\n" , v , mxdep[v]);
int pos1 = lower_bound(V[i].begin() , V[i].end() , dfn[v]) - V[i].begin();
int pos2 = upper_bound(V[i].begin() , V[i].end() , dfn[v] + sz[v] - 1) - V[i].begin() - 1;
assert(len == 0);
len = pos2 - pos1 + 1;
}
// printf("len:%lld\n" , len);
if(len == 0) len = vec[i].size();// , printf("qwq");
ans *= len * fac[vec[i].size() - 1] % mod , ans %= mod;
}
else
{
ans *= fac[vec[i].size()] , ans %= mod;
}
res *= fac[vec[i].size()] , res %= mod;
}
// printf("res:%lld\n" , res);
while(q --)
{
int k; scanf("%lld" , &k);
if(k > mx) printf("%lld " , res);
if(k == mx) printf("%lld " , ans);
if(k < mx) printf("0 ");
}
puts("");
tot = cnt = 0;
for(int i = 1 ; i <= n ; i ++) vec[i].clear() , V[i].clear() , head[i] = dep[i] = sz[i] = mxdep[i] = dfn[i] = LCA[i] = 0;
}
signed main()
{
fac[0] = 1;
for(int i = 1 ; i <= 500000 ; i ++) fac[i] = fac[i - 1] * i % mod;
int T; scanf("%lld" , &T);
while(T --) solve();
return 0;
}
实现细节参考代码,比如对于最后一层的特判,可以手推样例来了解为啥要这么做。