[CCO2025] Tree Decorations 题解
自己想到的神秘做法。下文中规定
转换一下题意,可以转为判断有多少种子树
假设
记
- 对于所有与
T_u 同构的T_v(v\in T_r) 对应的v (u,v 可以相同),\sum\limits_v(d'_v+1)\le c_u 。也就是说,这“一种”子树在整棵树上理论最少的出现次数不能超过实际上的出现次数。
于是就可以提出一个 map 套 vector 的确定性做法,哈希值的值域是
发现对于一个子树考察其中所有点是否满足条件有点困难,尝试反过来做,对于一个结点
现在的问题在于如何判断一个点 vector,按照 dfs 序依次存储所有哈希值为 vector 中二分 vector 的二分时可以顺带求出),将深度之和其减去
最后考虑
时间复杂度
代码想清楚了非常好写。放代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n,m,k,o=0,id=0; cin>>n>>m,k=__lg(n);
vector<vector<int> > g(n);
for(int i=1;i<n;i++){
int u,v; cin>>u>>v;
g[--u].emplace_back(--v);
g[v].emplace_back(u);
}
vector f(n,vector<int>(k+1,-1));
vector<int> d(n),l(n),t(n),e(n,1),h(n);
vector<ll> es(n);
map<vector<int>,int> mp;
auto dfs=[&](auto &&self,int u)->int{
for(int i=1;i<=k&&~f[u][i-1];i++)
f[u][i]=f[f[u][i-1]][i-1];
// 倍增求 2^i 级祖先
t[l[u]=o++]=u;
// l 表示 dfs 序
vector<int> v;
for(int i:g[u])
if(i!=f[u][0]){
f[i][0]=u,d[i]=d[u]+1;
v.emplace_back(self(self,i)),e[u]+=e[i];
}
es[u]=e[u];
for(int i:g[u])
if(i!=f[u][0])es[u]+=es[i];
sort(v.begin(),v.end());
if(mp.find(v)==mp.end())mp[v]=id++;
return h[u]=mp[v]; // map 维护树哈希
};
dfs(dfs,0); // 预处理所有需要的信息
vector<vector<int> > dv(n);
vector<vector<ll> > sv(n);
for(int u=0;u<n;u++){
dv[h[t[u]]].emplace_back(u);
sv[h[t[u]]].emplace_back((sv[h[t[u]]].empty()?0:sv[h[t[u]]].back())+d[t[u]]);
// d 按顺序存储了所有 dfs 序,s 是深度的前缀和
}
auto check=[&](int rt,int u){
int lb=lower_bound(dv[h[u]].begin(),dv[h[u]].end(),l[rt])-dv[h[u]].begin(),
ub=lower_bound(dv[h[u]].begin(),dv[h[u]].end(),l[rt]+e[rt])-dv[h[u]].begin()-1;
return sv[h[u]][ub]-(lb?sv[h[u]][lb-1]:0)-1ll*(d[rt]-1)*(ub-lb+1)<=dv[h[u]].size();
}; // 返回值为 false 表示 u 会使 rt 不合法
vector<bool> ban(n),rs(n);
for(int u=0;u<n;u++)
for(int i=k,c=u;~i;i--){
int v=f[c][i];
if(v<0)continue;
if(!ban[v]&&check(v,u))c=v;
else while(~v&&!ban[v])ban[v]=true,v=f[v][0];
} // 倍增过程,加了点小优化
for(int u=0;u<n;u++)
if(!ban[u]&&es[u]+m==n)rs[h[u]]=true;
// 检查 M 的条件是否满足
cout<<count(rs.begin(),rs.end(),true)<<endl;
return 0;
}