[ROIR 2024] 树根 (Day 1) 题解
[ROIR 2024] 树根 (Day 1) 题解
知识点
树剖,线段树,二分。
分析
首先可以想到二分上界 \(lim\),那么我们贪心地从深度最大的节点开始遍历,如果深度 \(>lim+1\),就暴力地往上找 \(lim-1\) 级祖先,然后和根连一条额外的边。一直重复上述过程直到所有点深度都 \(\le lim+1\)。过程中我们连的边就是最少的,可以用于检查合法性。
暴力求解单次是 \(O(nk\log{n})\) 的。
考虑优化,上数据结构,用线段树换根维护,那么过程中需要用到求换根后 \(k\) 级祖先,子树覆盖,这些都是比较套路的树上题目。
那么复杂度优化到了单次 \(O(k\log^2{n})\),那么总复杂度 \(O(nk\log^2{n})\)。
最后一个优化,发现相邻点的答案之差不超过 \(1\),那么我们维护一个类似双指针的东西,可以优化到 \(O(nk\log{n})\)。
代码
constexpr int N(2e5+10);
int n,m,idx,TYPE;
int fa[N],dl[N],dr[N],dfn[N],dep[N],siz[N],son[N],top[N];
vector<int> g[N];
void DFS0(int u) {
dep[u]=dep[fa[u]]+1,siz[u]=1,son[u]=0;
for(int v:g[u])if(v^fa[u])fa[v]=u,DFS0(v),siz[u]+=siz[v],son[u]=(siz[v]>siz[son[u]]?v:son[u]);
}
void DFS1(int u) {
dfn[dl[u]=++idx]=u;
if(son[u])top[son[u]]=top[u],DFS1(son[u]);
for(int v:g[u])if(v!=fa[u]&&v!=son[u])DFS1(top[v]=v);
dr[u]=idx;
}
struct SEG {
struct node {
int val,pos,tag;
node(int val=0,int pos=0,int tag=0):val(val),pos(pos),tag(tag) {}
void down(int _tag) { val+=_tag,tag+=_tag; }
} tr[N<<2];
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
void Up(int p) {
int q(tr[ls].val>=tr[rs].val?ls:rs);
tr[p].val=tr[q].val,tr[p].pos=tr[q].pos;
}
void Down(int p) { if(tr[p].tag)tr[ls].down(tr[p].tag),tr[rs].down(tr[p].tag),tr[p].tag=0; }
void Build(int p=1,int l=1,int r=n) {
tr[p]=node();
if(l==r)return tr[p]=node(dep[dfn[l]],dfn[l]),void();
Build(ls,l,mid),Build(rs,mid+1,r),Up(p);
}
void Plus(int L,int R,int d,int p=1,int l=1,int r=n) {
if(L<=l&&r<=R)return tr[p].down(d);
Down(p);
if(L<=mid)Plus(L,R,d,ls,l,mid);
if(mid<R)Plus(L,R,d,rs,mid+1,r);
Up(p);
}
#undef ls
#undef rs
#undef mid
} seg;
int lca(int u,int v) {
for(; top[u]^top[v]; v=fa[top[v]])if(dep[top[u]]>dep[top[v]])swap(u,v);
return dep[u]<dep[v]?u:v;
}
int anc(int u,int k) {
int d(dep[u]-k);
while(dep[top[u]]>d)u=fa[top[u]];
return dfn[dl[top[u]]+d-dep[top[u]]];
}
int Anc(int rt,int u,int k) {
int pa(lca(rt,u));
return dep[u]-k>=dep[pa]?anc(u,k):anc(rt,dep[u]+dep[rt]-(dep[pa]<<1)-k);
}
int Lcs(int u,int pa) { return anc(u,dep[u]-dep[pa]-1); }
void Plus(int rt,int u,int d) {
if(rt==u)return seg.Plus(1,n,d);
if(dl[u]<=dl[rt]&&dr[rt]<=dr[u]) {
int v(Lcs(rt,u));
return seg.Plus(1,n,d),seg.Plus(dl[v],dr[v],-d);
}
return seg.Plus(dl[u],dr[u],d);
}
bool Check(const int rt,const int lim) {
static int top(0),st[N];
FOR(i,1,m) {
if(seg.tr[1].val-1<=lim)break;
// DE(rt,lim,i,seg.tr[1].pos);
// DE(Anc(rt,seg.tr[1].pos,lim-1));
Plus(rt,st[++top]=Anc(rt,seg.tr[1].pos,lim-1),-n);
}
bool ans(seg.tr[1].val-1<=lim);
while(top)Plus(rt,st[top--],n);
return ans;
}
int ans[N];
void DFS(int u) {
if(u!=1) {
while(!Check(u,ans[u]))++ans[u];
while(ans[u]>1&&Check(u,ans[u]-1))--ans[u];
}
for(int v:g[u])if(v^fa[u]) {
seg.Plus(1,n,1),seg.Plus(dl[v],dr[v],-2);
ans[v]=ans[u],DFS(v);
seg.Plus(dl[v],dr[v],2),seg.Plus(1,n,-1);
}
}
signed main() {
#ifdef plus_cat
freopen(plus_cat ".in","r",stdin),freopen(plus_cat ".out","w",stdout);
#endif // plus_cat
/*DE("Input");*/
I(n,m,TYPE);
if(m>=n-1) {
if(!TYPE)return puts("1"),0;
FOR(i,1,n)O(1,' ');
return puts(""),0;
}
FOR(i,2,n) {
int u,v;
I(u,v),g[u].push_back(v),g[v].push_back(u);
}
/*DE("Build");*/
DFS0(1),DFS1(top[1]=1),seg.Build();
/*DE("Solve");*/
ans[1]=n-1;
for(int l(1),r(n-1),mid((l+r)>>1); l<=r; mid=(l+r)>>1)Check(1,mid)?ans[1]=mid,r=mid-1:l=mid+1;
if(!TYPE)return O(ans[1],'\n'),0;
DFS(1);
/*DE("Output");*/
FOR(i,1,n)O(ans[i],' ');
return puts(""),0;
}

浙公网安备 33010602011771号