[题解]CF1009F Dominant Indices
很明显的\(dp\)方程:
\(dp_{u,i}=\sum dp_{v,i-1}\)
然而空间和时间上都过不去,于是我们需要考虑优化。
我们可以用动态开点线段树来维护这个\(dp\)数组:
对于每个节点都开一棵以深度为下标的线段树,叶节点维护的是当前子树中深度(注意这里不是距离了)为\(i\)的节点数,而非叶节点维护区间中叶节点信息的最大值。(线段树以深度为下标是因为合并是节点维护的区间应该对应)
这时我们就可以进行深度优先遍历,先处理儿子,再将儿子上的线段树合并到当前节点。处理完所有儿子之后就可以到当前节点的线段树上查 询答案。
查询答案时就可以采用一个很简单的贪心策略:左儿子的值大于等于右儿子则向左儿子查询,否则向右儿子查询,到叶节点则直接返回。不过 此时查询到的答案是深度而题目要求的是距离,因此我们需要将答案减去当前节点的深度。
代码:
#include <cstdio>
using namespace std;
#define re register
#define il inline
#define isdigit(ch) (ch>=48&&ch<=57)
const int N=1000010;
il void read(int &x)
{
x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
il void write(int x){if(x/10)write(x/10);putchar(x%10+48);}
//IO优化
#define max(a,b) (a>b?a:b)
int cnt,h[N];
struct edge{int v,nxt;}e[N<<1];
il void add(int u,int v){e[++cnt]=(edge){v,h[u]};h[u]=cnt;}
//链式前向星
int n;
int rt[N],pool;
int ls[N<<5],rs[N<<5],val[N<<5];
//val表示当前节点维护的深度区间中最大的节点数量
#define update(p) (val[p]=max(val[ls[p]],val[rs[p]]))
//更新节点信息
il int merge(int p,int q,int l,int r)
{
if(!p||!q) return q|p;
if(l==r){val[p]+=val[q];return p;}
re int mid=(l+r)>>1;
ls[p]=merge(ls[p],ls[q],l,mid);
rs[p]=merge(rs[p],rs[q],mid+1,r);
update(p);return p;
}
//合并线段树
il void insert(int &p,int l,int r,int x)
{
if(!p) p=++pool;
if(l==r){++val[p];return;}
re int mid=(l+r)>>1;
if(x>mid) insert(rs[p],mid+1,r,x);
else insert(ls[p],l,mid,x);
update(p);
}
il int query(int p,int l,int r)
{
if(l==r) return l;
re int mid=(l+r)>>1;
if(val[ls[p]]<val[rs[p]]) return query(rs[p],mid+1,r);
else return query(ls[p],l,mid);
}
//查询答案
int ans[N],dep[N];
il void dfs(int x,int fa)
{
dep[x]=dep[fa]+1;
insert(rt[x],1,n,dep[x]);
for(re int i=h[x];i;i=e[i].nxt)
if(e[i].v^fa)//if(e[i].v!=fa)
{
dfs(e[i].v,x);
rt[x]=merge(rt[x],rt[e[i].v],1,n);
}
ans[x]=query(rt[x],1,n)-dep[x];
//由于答案是要当前节点往下的距离,而query返回的是深度,因此要减去dep[x]
}
int main()
{
read(n);
for(re int i=1,u,v;i<n;++i)
{
read(u),read(v);
add(u,v);add(v,u);
}
dfs(1,0);
for(re int i=1;i<=n;++i)
write(ans[i]),puts("");
return 0;
}