cf1009 F. Dominant Indices

Link

Link

长链剖分

对每个点记录其子树中深度最深的叶子,每个点到其最深的叶子拉出一条长链

这样整棵树就被划分成很多条链,这种方法被称为长链剖分

其优势在于,当我需要维护和深度有关的信息时,父亲可以 O ( 1 ) O(1) 继承儿子的信息,进而减少了不必要的计算,总的计算次数是 O ( n ) O(n) 的。

在许多题目中我往往需要解决一些和深度有关的问题,最开始我通过 O ( 1 ) O(1) 的操作,把最长的一条链的信息已经得到了。后面每次算完一棵子树,都相当于得到一条新链(因为我只对每个深度维护一个信息,相当于深度相同的点的信息合并了),为了将这条新链和我已经有的一条旧链合并,我就枚举 i [ 1 , l e n ] i \in [1,len] l e n len 是新链的长度)进行处理,假设合并同深度的两个信息的复杂度是 O ( 1 ) O(1) ,那么总复杂度是线性的。

为啥是线性的呢?因为我每次枚举都合并两个点,也就是说会减少一个点,而我一共就只有 n n 个点,当然复杂度就是线性的了。

长链的作用在于:使得我后面合并的时候每次枚举都是在合并两个点,从而保证了复杂度。

以此题为例说一下流程

f u , i f_{u,i} 表示以 u u 为根的子树,深度为 i i 的儿子的个数,这个题让你对每个 u u 都求最大的 f u , i f_{u,i} 对应的那个 i i

预处理:

先一遍 d f s dfs 求出每个点的最深的那个儿子,找到所有长链的顶点

下面是计算过程(大体描述):

在长链的 t o p top 点申请一段连续的内存,以后这条链上的其他点每次直接继承这段连续内存的一个后缀就行了。

然后遍历其他的儿子,递归计算完了之后,相当于我得到了一条链的信息,暴力合并到已有的信息中。

Code

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define eps 1e-8
#define maxn 1000010
#define maxe 2000010
#define cl(x) memset(x,0,sizeof(x))
#define rep(i,a,b) for(i=a;i<=b;i++)
#define drep(i,a,b) for(i=a;i>=b;i--)
#define em(x) emplace(x)
#define emb(x) emplace_back(x)
#define emf(x) emplace_front(x)
#define fi first
#define se second
#define de(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll read(ll x=0)
{
    ll c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-0x30;
    return f*x;
}
struct Graph
{
    int etot, head[maxn], to[maxe], next[maxe], w[maxe];
    void clear(int N)
    {
        for(int i=1;i<=N;i++)head[i]=0;
        etot=0;
    }
    void adde(int a, int b, int c=0){to[++etot]=b;w[etot]=c;next[etot]=head[a];head[a]=etot;}
    #define forp(_,__) for(auto p=__.head[_];p;p=__.next[p])
}G;
struct Longest_Chain_Decomposition
{
    ll tot, len[maxn], son[maxn], depth[maxn], istop[maxn];
    void dfs(Graph& G, ll u, ll fa)
    {
        son[u]=0;
        len[u]=1;
        depth[u]=depth[fa]+1;
        istop[u]=false;
        forp(u,G)
        {
            ll v(G.to[p]); if(v==fa)continue;
            dfs(G,v,u);
            if(len[v]+1>len[u])len[u]=len[v]+1, son[u]=v;
        }
        forp(u,G)
        {
            ll v(G.to[p]); if(v==fa)continue;
            if(v!=son[u])istop[v]=true;
        }
    }
    void run(Graph& G, int root)
    {
        tot=0;
        depth[0]=0, dfs(G,root,0);
        istop[root]=true;
    }
}lcd;
ll *f[maxn], pool[maxn], tot, n, ans[maxn];
void dp(ll u, ll fa)
{
    ll i;
    if(lcd.istop[u])
    {
        f[u] = pool+tot;
        tot += lcd.len[u];
    }
    f[u][0]=1;
    if(lcd.son[u])
    {
        auto v(lcd.son[u]);
        f[v] = f[u]+1;
        dp(v,u);
        if(f[v][ans[v]]>1)ans[u]=ans[v]+1;
    }
    forp(u,G)
    {
        ll v=G.to[p];
        if(v==fa or v==lcd.son[u])continue;
        dp(v,u);
        rep(i,0,lcd.len[v]-1)
        {
            f[u][i+1] += f[v][i];
            if(f[u][i+1]>f[u][ans[u]] or f[u][i+1]==f[u][ans[u]] and i+1<ans[u])
                ans[u] = i+1;
        }
    }
}
int main()
{
    ll i, u, v;
    n = read();
    rep(i,1,n-1)
    {
        u=read(), v=read();
        G.adde(u,v), G.adde(v,u);
    }
    lcd.run(G,1);
    dp(1,0);
    rep(i,1,n)printf("%lld\n",ans[i]);
    return 0;
}
发布了948 篇原创文章 · 获赞 77 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/105259829