Codeforces 1009F 长链剖分优化dp做法

题意:

给出一棵树,设对于一个点, f u , i f_{u,i} fu,i表示距离 u u u i i i的点的数量,对于每个点都有一个无限长的序列 [ f u , 0 , f u , 1 . . . . . ] [f_{u,0},f_{u,1}.....] [fu,0,fu,1.....]

找到每个点的序列中的最大值点,如果有多个最大值,那么取点最小的

长链剖分做法:

长链剖分优化 d p dp dp实际上就是一个启发式合并的思路,树上启发式合并一般是对重儿子而言的,每次保留重儿子,即 O ( 1 ) O(1) O(1)继承重儿子,暴力记录轻儿子们,最后的作用是以 O ( n l o g n ) O(nlogn) O(nlogn)统计子树信息。在长链剖分, O ( 1 ) O(1) O(1)继承长儿子,暴力记录短儿子,从链的角度合并为子树,由于每个点只在一条长链上,每个点都被合并一次,有一个优秀的复杂度 O ( n ) O(n) O(n),但有局限性,一般只能合并和深度有关的,而树上启发式合并能做到的更到,但时间也需要更多一些。

d p [ u ] [ i ] dp[u][i] dp[u][i]为距离 u u u结点距离为 i i i的儿子个数,那么转移也很简单

d p [ u ] [ i ] = ∑ v ∈ s o n u d p [ v ] [ i − 1 ] dp[u][i]=\sum_{v\in son_{u}} dp[v][i-1] dp[u][i]=vsonudp[v][i1]

显然, d p [ v ] dp[v] dp[v]会被累加进 d p [ u ] dp[u] dp[u],按照启发式合并的思想,应该把深度最深的儿子的子树保留,才能节省时间,不妨利用指针,开一个数组 c n t cnt cnt,选取一个子段来储存 d p [ u ] [ i ] dp[u][i] dp[u][i],令 d p [ u ] dp[u] dp[u]是一个指针,指向 c n t cnt cnt数组上的起点,长度即 m a x 1 [ u ] max1[u] max1[u],长儿子直接在父亲的指针上写信息,父亲 O ( 1 ) O(1) O(1)获得了长儿子信息,即 d p [ s o n [ u ] ] = d p [ u ] + 1 dp[son[u]]=dp[u]+1 dp[son[u]]=dp[u]+1,那么只需要所有长链长度和的 c n t cnt cnt即可,即 n n n的长度,遵循每条长链的所有点共用 c n t cnt cnt的一块,空间复杂度就是 O ( n ) O(n) O(n)

#include<bits/stdc++.h>
#define ll long long
using namespace std;

struct way
{
    int to,next;
}edge[2000005];
int cntt,head[1000005];

void add(int u,int v)
{
    edge[++cntt].to=v;
    edge[cntt].next=head[u];
    head[u]=cntt;
}

int cnt[1000005];
int *dp[1000005],ans[1000005],*pp=cnt;
int n,max1[1000005],son[1000005];

void dfs1(int u,int fa)
{
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        dfs1(v,u);
        if(max1[v]>max1[son[u]]) son[u]=v;
    }
    max1[u]=max1[son[u]]+1;
}

void dfs(int u,int fa)
{
    dp[u][0]=1;
    if(son[u])
    {
        dp[son[u]]=dp[u]+1;
        dfs(son[u],u);
        if(dp[son[u]][ans[son[u]]]==1) ans[u]=0;
        else ans[u]=ans[son[u]]+1;
    }
    //max1[u]是u到他的长链底的距离
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa||v==son[u]) continue;
        dp[v]=pp; pp+=max1[v];
        dfs(v,u);
        for(int j=1;j<=max1[v];j++)//暴力记录所有短儿子
        {
            dp[u][j]+=dp[v][j-1];
            if(dp[u][j]>dp[u][ans[u]]) ans[u]=j;
            else if(dp[u][j]==dp[u][ans[u]]&&j<ans[u]) ans[u]=j;
        }
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int u,v; scanf("%d%d",&u,&v);
        add(u,v); add(v,u);
    }
    dfs1(1,0); 
    dp[1]=pp; pp+=max1[1];//先分配空间再dfs
    dfs(1,0);
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}
### Codeforces Problem 1009 Details and Solution The referenced content discusses a set of problems related to number theory on the Codeforces platform with difficulties ranging from 2000 to 3000+. The focus is primarily on optimizing algorithms due to large input sizes where complexities such as \(O(n \sqrt{n})\) are not feasible without additional optimizations[^1]. For **Problem 1009**, while specific details about this exact problem may vary depending on updates or changes within the Codeforces database, it generally involves handling operations over tree structures efficiently given constraints like those described: - Input consists of two integers \(n\) (number of nodes) and \(m\) (number of queries), followed by detailed descriptions of each node's children. - Queries include three types: adding an edge between two vertices ("1 v u"), removing edges based on height conditions ("2 v h"), and finding paths satisfying certain properties ("3 k"). Given these parameters, efficient data structure usage becomes critical. Below outlines how one might approach solving similar structured questions effectively. #### Efficient Data Structures Utilization To handle up to \(10^5\) elements per dimension (\(n,m\)), advanced techniques must be employed: - Preprocessing using Depth First Search (DFS) traversal can help compute auxiliary information quickly during runtime. - Segment trees or Binary Indexed Trees could assist managing dynamic range updates/queries required under different operation modes mentioned above. Here’s some sample pseudo-code demonstrating initialization phase alongside query processing logic utilizing DFS precomputation technique combined with segment tree implementation for fast querying capabilities: ```python from collections import defaultdict class Node: def __init__(self): self.children = [] def dfs(node_id, parent=-1): global depth, subtree_size start_time[node_id] = time_counter[0] time_increases() subtree_size[node_id]=1 for child in adj_list[node_id]: if(child !=parent): depth[child]=depth[node_id]+1 dfs(child,node_id) subtree_size[node_id]+=subtree_size[child] # Initialize variables & read inputs here... adj_list=defaultdict(list) for _ in range(m): op,*params=input().split() params=list(map(int,params)) if(op=='1'): # Add Edge Logic Here Using Params[v,u] elif(op=='2'): pass else: # Operation Type '3' result=process_query_k(params[k]) print(result) ``` This code snippet provides foundational steps towards constructing solutions tailored specifically toward addressing challenges posed through complex graph manipulations involving numerous sequential actions against hierarchical datasets represented via adjacency lists. §§Related Questions§§ 1. What other common optimization strategies exist when dealing with computational limits exceeding standard algorithmic approaches? 2. How does implementing lazy propagation affect performance improvements inside segment-tree-based systems used across competitive programming platforms? 3. Can you provide examples illustrating alternative methods besides DFS preorder/postorder traversals that achieve equivalent results more suitably suited under particular circumstances? 4. In what ways do persistent data structures contribute differently compared traditional mutable ones concerning memory consumption versus speed tradeoffs seen regularly throughout high-level contests including CF rounds?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值