题解:P17240 [IOI 2026] 弹球机 / ballmachine
vp 的时候猛攻 T2 去了,最后剩下不到 1.5h 做 T1 结果
我 vp 时的
考虑先按照
考虑
我们考虑一个形式比较简单的操作,按照
否则如果下一个位置和自己颜色相同,那么一定有下一个位置是自己的某个儿子,我们依然递归这个儿子即可。
这个做法通过先走不同颜色的儿子避免了我们算不出不同的颜色链之间的 LCA 深度的问题。
接下来我们开始尝试解决原问题,注意到
我们考虑一个极端情况,假如树
考虑设计一个递归的编号函数
这个想法非常有道理,因为两个数
将所有
现在我们已经可以区分出
这个做法需要大概
#include<bits/stdc++.h>
bool insert(int U, int X);
std::vector<int> collect();
std::vector<int> find_structure(int M);
using namespace std;
const int maxn = 1114;
int nd;
int rt;//rt 初始为极大值
vector<int> E[maxn];
int col[maxn];
const int warma = 14;//每 warma 个叶子做一次
int spec;
bool cmp(int x,int y){
return E[x].size()>E[y].size();
}
int leaf[maxn];
vector<int> vec;
void dfs(int u){
vec.push_back(u);
if(E[u].size()==0){
leaf[u]=u;
return ;
}
vector<int> son;
for(int v:E[u]){
dfs(v);
leaf[u]=leaf[v];
if(E[v].size()==0) col[v]=-1;
else{
son.push_back(v);
}
}
sort(son.begin(),son.end(),cmp);
int id=0;
int index=0;
for(int i=0;i<(int)son.size();i++){
int v=son[i];
map<int,int> f;
for(int w:E[v]){
if(f.find(col[w])==f.end()) f[col[w]]=id++;
if(id>=12){
id-=12;
if(i!=0) index++;
}
}
for(int w:E[v]) col[w]=f[col[w]];
col[v]=index;
}
return ;
}
set<int> S;
int Last[maxn];
void build(int u,vector<int> &dfn,int &pos,int R){
S.insert(u);
int now=dfn[pos];
while(pos+1<(int)dfn.size()){
if(dfn[pos+1]>now) return ;
int v=-1;
if(pos+1==Last[dfn[pos+1]]) v=R-dfn[pos+1]-1;
else v=nd++;
E[u].push_back(v);
pos++;
build(v,dfn,pos,R);
}
}
void solve(int u,vector<int> dfn,int &pos,int R,int lim){
S.insert(u);
int now=dfn[pos];
if(dfn[pos]<lim){
//新增的节点
while(pos+1<(int)dfn.size()){
if(dfn[pos+1]>now) return ;
int v=-1;
if(pos+1==Last[dfn[pos+1]]) v=R-dfn[pos+1]-1;
else v=nd++;
E[u].push_back(v);
pos++;
solve(v,dfn,pos,R,lim);
}
}else{
//原有的节点
int d=E[u].size();
vector<int> son;
while(d>0){
if(dfn[pos+1]<lim){
int v=-1;
if(Last[dfn[pos+1]]==pos+1) v=R-dfn[pos+1]-1;
else v=nd++;
son.push_back(v);
pos++;
solve(v,dfn,pos,R,lim);
}else{
d--;
//判别进入了哪个儿子
int Leaf=-1;
for(int v:E[u]){
if(E[v].size()==0) Leaf=col[v];
}
if(dfn[pos+1]==Leaf){
pos++;
//进入了哪个叶子都不重要
}else{
for(int v:E[u]){
if(E[v].size()>0){
int mi=1e9;
for(int w:E[v]) mi=min(mi,col[w]);
//找到 pos+1 后面第一个 >=lim 的
int l=pos+2;
while(dfn[l]<lim) l++;
if(dfn[pos+1]==col[v]&&dfn[l]==mi){
pos++;
solve(v,dfn,pos,R,lim);
break;
}
}
}
}
}
}
for(int v:son) E[u].push_back(v);
}
}
std::vector<int> find_structure(int M){
nd=M;
int rt=1000;
//[0,warma)
int L=0,R=min(warma,M);
for(int i=L;i<R;i++){
while(insert(i,R-i-1)==true);
}
vector<int> dfn=collect();
memset(Last,0,sizeof(Last));
for(int j=0;j<(int)dfn.size();j++) Last[dfn[j]]=j;
int pos=0;
build(rt,dfn,pos,R);
for(int i=warma;i<M;i+=warma){
int L=i,R=min(M,i+warma);
vec.clear();
col[rt]=0;
dfs(rt);
int mx=0;
for(int x:S){
if(col[x]!=-1) mx=max(mx,col[x]);
}
mx++;
for(int x:S){
if(col[x]==-1) col[x]=mx;
}
mx++;
//[0,R-L)
for(int x:S){
col[x]+=R-L;
}
//放置
for(int x:vec){
insert(leaf[x],col[x]);
}
for(int i=L;i<R;i++){
while(insert(i,R-i-1)==true);
}
dfn=collect();
memset(Last,0,sizeof(Last));
for(int j=0;j<(int)dfn.size();j++) Last[dfn[j]]=j;
pos=0;
solve(rt,dfn,pos,R,R-L);//这个过程中更新 S
}
int n=nd+1;
vector<int> fa(n-1);
for(int i=0;i<nd;i++){
for(int j:E[i]) fa[j]=i;
}
for(int j:E[rt]) fa[j]=n-1;
return fa;
}