F. Dominant Indices(動態開點線段樹 or dsu on tree)

題目鏈接:F. Dominant Indices
題意:有根樹,根爲1,定義d[x][i]d[x][i]爲x的子樹中距離x爲i的點的個數,對於每個點,求出最小的i使得d[x][i]d[x][i]最大。

dis[x]dis[x]表示x與樹根的距離,對於每個子樹動態開點維護距離爲valval的點的個數,以及最大值,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跑得還是有點慢的:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章