[CCO 2026] Tree Traversals 题解
有点诈骗的好题。
观察条件二,发现按深度把点分成若干层,深度小的在排列里必须优先出现,要决定的只有每一层内部的顺序。
然后要发现它的诈骗之处,设所有层的层内部两两之间最大距离的最大值为 \(mx\),我们可以根据询问的 \(k\) 的大小分成三种情况。
1.\(k<mx\),同一层次内部至少会经过一次这个最大距离(即走到这一层的 lca 再往下的距离),所以若 \(k<mx\) 则一定无解,答案为 \(0\)。
2.\(k>mx\),则说明每一个层内都可以乱排,答案就是每一层的全排之积。
3.\(k=mx\),此时需要我们认真考虑内部的排列方案,一般这种情况下要想到 DP了,而且因为层内是合法的,只要层与层之间也合法即可,那我们似乎主要关注的是每一层由谁开头以及由谁结尾。
用数组 \(E_d\) 按 dfn 序存储所有深度为 \(d\) 的点,此时同一层内距离的最大值,就是 \(E_d\) 内部第一个点到最后一个点的距离。
设 \(f_{u,0/1}\),\(f_{u,0}\) 表示当 \(u\) 是第 \(dep_u\) 层的开头节点,前 \(dep_u-1\) 层已经排好的方案数。\(f_{u,1}\) 表示当 \(u\) 是第 \(dep_u\) 层的结尾节点,前 \(dep_u\) 层已经排好的方案数。
答案要求 \(\sum_{v \in E_{md}} f_{v,1}\),考虑 \(f_{u,1}\) 怎么转移得到。当这一层只有一个节点时 \(f_{u,1}=f_{u,0}\)。否则 \(f_{u,1} = \sum_{v \in E_{dep_u} \land v \neq u} f_{v,0} \times (len-2)!\),其中 \(len\) 表示这一层的节点数。
那就只要考虑求出 \(f_{u,0}\) 了,显然,对于所有 \(dis(u,v) \le mx\) 的上一层节点 \(v\),都有 \(f_{u,0} \leftarrow f_{v,1}\)。一个性质就是,距离最近的点是其父亲节点,而在 \(E_{dep_{fa}}\) 内父亲节点的两边 \(dis(u,v)\) 是单增的,所以我们可以二分找出最左边和最右边满足条件的点的位置。
所以流程大概是这样,因为下一层必须要用到上一层所有点的值,必须一层层的更新,所以我们用一个类似 BFS 的方式更新,每到达一个新的层就直接把每个点的 \(f_{u,1}\) 都用这一层的 \(f_{u,0}\) 更新,而 \(f_{u,0}\) 是被上一层的父亲节点给更新过的。然后再去更新下一层的儿子节点。
但是如果直接更新的话复杂度过高了,注意到两个式子都可以前缀和优化,就做完了。
时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int T,n,m,mx;
const int N = 1e6+10,mod=1e9+7;
int fac[N];
inline void add(int &x,int y){
x=(x+y>=mod)?x+y-mod:x+y;
}
vector<int>g[N],e[N];
int s[N];
int k,dep[N],cnt[N],dfn[N],tot,md,rdfn[N],id[N],st[N][22];
int Min(int x,int y){
return (dfn[x]<dfn[y])?x:y;
}
int lca(int x,int y){
x=dfn[x],y=dfn[y];
if(x==y)return rdfn[x];
if(x>y)swap(x,y);
x++;
int k=__lg(y-x+1);
return Min(st[x][k],st[y-(1<<k)+1][k]);
}
void dfs(int x,int f){
dep[x]=dep[f]+1;md=max(md,dep[x]);
dfn[x]=++tot;rdfn[tot]=x;st[tot][0]=f;
e[dep[x]].push_back(x);
id[x]=e[dep[x]].size()-1;
for(auto v:g[x]){
if(v==f)continue;
dfs(v,x);
}
}
int dist(int x,int y){
return dep[x]+dep[y]-2*dep[lca(x,y)];
}
int f[N][2];
bitset<N>vis;
void dp(int u,int fa){
int d=dep[fa];
int l=0,r=id[fa],bl=r,br=r;
if(cnt[d]==1){
add(f[u][0],f[fa][1]);
return;
}
while(l<=r){
int mid=(l+r)/2;
int v=e[d][mid];
if(dist(v,u)<=mx){
bl=mid;
r=mid-1;
}else{
l=mid+1;
}
}
l=id[fa],r=e[d].size()-1;
while(l<=r){
int mid=(l+r)/2;
int v=e[d][mid];
if(dist(v,u)<=mx){
br=mid;
l=mid+1;
}else{
r=mid-1;
}
}
bl++,br++;
add(f[u][0],(s[br]-s[bl-1]+mod)%mod);
}
void bfs(){
vis[1]=1;int lst=0;
queue<int>q;q.push(1);
while(!q.empty()){
int u=q.front();q.pop();
if(dep[u]!=lst){//一次更新一层的f[u][1]
lst=dep[u];
int len=e[lst].size();s[0]=0;
for(int i=0;i<len;i++){
int v=e[lst][i];
s[i+1]=(s[i]+f[v][0])%mod;
}
if(len!=1){
for(int i=0;i<len;i++){
int v=e[lst][i];
f[v][1]=(s[len]-f[v][0]+mod)%mod*fac[len-2]%mod;
}
s[0]=0;
for(int i=0;i<len;i++){
int v=e[lst][i];
s[i+1]=(s[i]+f[v][1])%mod;
}
}else{
f[e[lst][0]][1]=f[e[lst][0]][0];
}
}
for(auto v:g[u]){
if(vis[v])continue;
dp(v,u);//下一层的f[v][0]
}
for(auto v:g[u]){
if(vis[v])continue;
q.push(v);
vis[v]=1;
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
fac[0]=1;
for(int i=1;i<=N-10;i++){
fac[i]=(fac[i-1]*i)%mod;
}
while(T--){
cin>>n>>m;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,0);
for(int j=1;(1<<j)<=tot;j++){
for(int i=1;i+(1<<j)-1<=tot;i++){
st[i][j]=Min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
}
for(int i=1;i<=md;i++){
cnt[i]=e[i].size();
}
int res=1;
for(int i=1;i<=md;i++){
res=(res*fac[cnt[i]])%mod;
}
for(int i=1;i<=md;i++){
mx=max(mx,dist(e[i][0],e[i].back()));
}
f[1][0]=1;
bfs();
int ans=0;
for(auto v:e[md]){
add(ans,f[v][1]);
}
for(int i=1,k;i<=m;i++){
cin>>k;
if(k<mx)cout<<0<<" ";
else if(k>mx)cout<<res<<" ";
else cout<<ans<<" ";
}
cout<<endl;
mx=0;vis.reset();tot=0;md=0;
for(int i=1;i<=n;i++){
g[i].clear();
e[i].clear();cnt[i]=0;
f[i][0]=f[i][1]=0;
s[i]=0;dep[i]=0;
}
}
return 0;
}

浙公网安备 33010602011771号