题目链接:F. Dominant Indices
题意:有根树,根为1,定义
d
[
x
]
[
i
]
d[x][i]
d[x][i]为x的子树中距离x为i的点的个数,对于每个点,求出最小的i使得
d
[
x
]
[
i
]
d[x][i]
d[x][i]最大。
d i s [ x ] dis[x] dis[x]表示x与树根的距离,对于每个子树动态开点维护距离为 v a l val val的点的个数,以及最大值,dfs回溯合并线段树即可。
参考了一下橘子猫大大:一只叫橘子的猫
还有一种dsu on tree的做法,我yy了一下,其实相当于对于每个点求其子树距离权值的众数,之后再补。
动态开点线段树
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+1;
struct Tree{
int lc,rc,sum;
int id;
}tree[maxn*21];
int tot;
int build(){
int k=++tot;
tree[k].lc=tree[k].rc=tree[k].sum=0;
return k;
}
void pushup(int k){
tree[k].sum=max(tree[tree[k].lc].sum,tree[tree[k].rc].sum);
if(tree[k].sum==tree[tree[k].lc].sum) tree[k].id=tree[tree[k].lc].id;
else tree[k].id=tree[tree[k].rc].id;
}
void insert(int p,int l,int r,int val,int d){
if(l==r){
tree[p].sum+=d;
tree[p].id=l;
return ;
}
int mid=l+r>>1;
if(val<=mid){
if(!tree[p].lc) tree[p].lc=build();
insert(tree[p].lc,l,mid,val,d);
}
else{
if(!tree[p].rc) tree[p].rc=build();
insert(tree[p].rc,mid+1,r,val,d);
}
pushup(p);
}
int marge(int p,int q,int l,int r){
if(!p||!q) return p|q;
if(l==r){
tree[p].sum+=tree[q].sum;
return p;
}
int mid=l+r>>1;
tree[p].lc=marge(tree[p].lc,tree[q].lc,l,mid);
tree[p].rc=marge(tree[p].rc,tree[q].rc,mid+1,r);
pushup(p);
return p;
}
int root[maxn];
vector<int> G[maxn];
int n;
int res[maxn];
void dfs(int u,int fa,int x){
int val=x;
root[u]=build();
insert(root[u],1,n,val,1);
for(auto v:G[u]){
if(v==fa) continue;
dfs(v,u,x+1);
root[u]=marge(root[u],root[v],1,n);
}
res[u]=tree[root[u]].id-val;
}
void add(int u,int v){
G[u].push_back(v);
G[v].push_back(u);
}
int main(){
scanf("%d",&n);
int u,v;
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
add(u,v);
}
dfs(1,0,1);
for(int i=1;i<=n;++i) printf("%d\n",res[i]);
return 0;
}
dsu on tree
将距离看做权值,就相当于是无修改求子树众数,若有多个求最小的那个。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
vector<int> G[maxn];
int num[maxn],siz[maxn],dep[maxn];
int ans,Num;
int Son[maxn];
void dfs(int u,int fa){
siz[u]=1;
int maxx=0;
for(auto v:G[u]){
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
if(siz[v]>maxx){
maxx=siz[v];
Son[u]=v;
}
}
}
int son;
void count(int u,int fa){
dep[u]=dep[fa]+1;
++num[dep[u]];
if(num[dep[u]]>Num||(num[dep[u]]==Num&&dep[u]<ans)) ans=dep[u],Num=num[dep[u]];
for(auto v:G[u]){
if(v==fa||v==son) continue;
count(v,u);
}
}
void del(int u,int fa){
dep[u]=dep[fa]+1;
--num[dep[u]];
for(auto v:G[u]){
if(v==fa) continue;
del(v,u);
}
}
int res[maxn];
//先计算轻儿子,再计算重儿子,并且保留重儿子的那部分;
void calc(int u,int fa,bool f){
dep[u]=dep[fa]+1;
for(auto v:G[u]){
if(v==fa||v==Son[u]) continue;
calc(v,u,false);
}
if(Son[u]) calc(Son[u],u,true),son=Son[u];
count(u,fa);
son=0,res[u]=ans-dep[u];
if(!f) del(u,fa),Num=0,ans=0;
}
int main(){
int n,u,v;
scanf("%d",&n);
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
calc(1,0,0);
for(int i=1;i<=n;++i) printf("%d\n",res[i]);
return 0;
}
不过dsu on tree跑得还是有点慢的: