題目鏈接:F. Dominant Indices
題意:有根樹,根爲1,定義爲x的子樹中距離x爲i的點的個數,對於每個點,求出最小的i使得最大。
表示x與樹根的距離,對於每個子樹動態開點維護距離爲的點的個數,以及最大值,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跑得還是有點慢的: