zoukankan      html  css  js  c++  java
  • 长链剖分学习笔记

    什么事长链剖分:

    • 对于每个节点令其子树高度最大的儿子的边为实边,其余边为虚边。
    • 于是树可以剖成由实边组成的若干长链。
    • 同重剖类似,有一个结论:对于任意节点 (u),其到根的路径的上的虚边数量(长链数量)是 (O(sqrt n)) 级别。

    CF1009F Dominant Indices

    对于每个节点 (u),求出一个最小的 (k),使得 (u) 子树中到 (u) 距离为 (k) 的点的数量最大。

    对于每个点 (f(u,d)) 表示 (u) 子树内到 (u) 的距离为 (d) 的节点个数。如果直接存储会炸裂。我们发现其实求 (u) 的信息完全可以直接利用一些在子节点求得的信息然后再往上增量。这点信息,不需要 copy,直接用指针即可。

    我们考虑对于每一个长链申请一个空间。从指针理解,(f_u) 表示 (f_u(d)) 数组的头指针。对于节点 (u),设它的实边儿子为 (v),则让 (f_v=f_u+1),即长成这样:

    然后对于每个节点,我们不仅从 (v) 上直接得到信息,还需要合并所有虚边连向的长链。这个普通转移即可。

    对于复杂度,每条长链最多被合并一次,而合并一次的复杂度为其链长,所以总复杂度为 (sum len=O(n))。而空间复杂度也一样,每条链所需空间为链长,同样得到 (O(n))

    对于分配内存,维护一个指针,指向一个数组的位置,然后将长 (dep+1) 的一段分配给它。

    内存分配还是开大一点好。

    https://codeforces.com/contest/1009/submission/133302570

    const int N=1e6+9;
    int n,h[N],son[N],g[N<<1],*f[N],ans[N],*cur=g;
    vi e[N];
    
    void dfs1(int u,int fa) {
    	for(auto v:e[u]) if(v!=fa) {
    		dfs1(v,u);
    		if(h[v]>=h[son[u]]) son[u]=v, h[u]=h[v]+1;
    	}
    }
    void dfs2(int u,int fa) {
    	if(son[u]) {
    		f[son[u]]=f[u]+1, dfs2(son[u],u), ans[u]=ans[son[u]]+1;
    	}
    	int mh=0;
    	for(auto v:e[u]) if(v!=fa&&v!=son[u]) {
    		f[v]=cur, cur+=h[v]+1;
    		dfs2(v,u);
    		rep(i,0,h[v]) f[u][i+1]+=f[v][i];
    		mh=max(mh,h[v]+1);
    	}
    	f[u][0]=1;
    	rep(i,0,mh)
    		if(f[u][i]>f[u][ans[u]]||f[u][i]==f[u][ans[u]]&&i<ans[u]) ans[u]=i;
    }
    
    int main() {
    	n=read();
    	rep(i,2,n) {
    		int u=read(), v=read();
    		e[u].emplace_back(v), e[v].emplace_back(u);
    	}
    	dfs1(1,0);
    	f[1]=cur, cur+=h[1]+1; dfs2(1,0);
    	rep(i,1,n) printf("%d
    ",ans[i]);
    	return 0;
    }
    

    经典题

    求树上长度(路径点数)恰好为 (k) 的路径数量。

    考虑在合并链的时候统计一下跨过 (u) 的链的数量即可。

    const int N=3e5+9;
    int n,k,ans,son[N],h[N],g[N<<1],*f[N],*cur=g;
    vi e[N];
    
    void dfs1(int u,int fa) {
    	for(auto v:e[u]) if(v!=fa) {
    		dfs1(v,u);
    		if(h[v]>=h[son[u]]) son[u]=v;
    	} h[u]=h[son[u]]+1;
    }
    void dfs2(int u,int fa) {
    	if(son[u]) {
    		f[son[u]]=f[u]+1, dfs2(son[u],u); if(h[u]>=k) ans+=f[u][k];
    	} f[u][0]++;
    	for(auto v:e[u]) if(v!=fa&&v!=son[u]) {
    		f[v]=cur, cur+=h[v]+1;
    		dfs2(v,u);
    		rep(i,0,min(k,h[v])) if(k-i-1<=h[u]) ans+=f[u][k-i-1]*f[v][i];
    		rep(i,0,h[v]) f[u][i+1]+=f[v][i];
    	}
    }
    z
    signed main() {
    	n=read(), k=read();
    	rep(i,2,n) {
    		int u=read(), v=read();
    		e[u].emplace_back(v), e[v].emplace_back(u);
    	}
    	dfs1(1,0), f[1]=cur, cur+=h[1]+1, dfs2(1,0);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    POI2014 Hotels

    求树上选三点两两距离相等的方案数。

    (f(u,i)) 表示子树中距离 (u)(i) 的节点个数。然后考虑一种特殊情况,即三个点 (x,y,z) (相互距离为 (d) 且总体 (lca=u))满足 (lca(y,z))(u) 的距离加上 (z)(u) 的距离等于 (d)(即 (lca(y,z))(z) 的链跨过 (u))。针对这种情况,我们设计状态 (g(u,i)) 表示存在多少这样的 (y,z) 满足到 (lca(y,z)) 距离相等(设之为 (d)),且 (lca(y,z))(u) 的距离为 (d-i)

    所以答案为(若 (u,v) 同时出现,此时意味着 (f(u)) 只是一个前缀,在和 (v) 合并时统计答案)

    [ans=g(u,0) +sum_i sum_{vin son_u} f(u,i-1) imes g(v,i)+f(u,i-1) imes g(v,i)\ g(u,i)=sum g(v,i+1)+f(u,i) imes f(v,i-1) ]

    长剖增量 DP 即可。

    const int N=4e5+9,mod=1e9+7;
    int n,*f[N],*g[N],h[N],p[N],*cur=p,son[N],ans;
    vi e[N];
    
    void dfs1(int u,int fa) {
    	for(auto v:e[u]) if(v!=fa) {
    		dfs1(v,u);
    		if(h[v]>=h[son[u]]) son[u]=v;
    	}
    	h[u]=h[son[u]]+1;
    }
    void dfs2(int u,int fa) {
    	if(son[u]) {
    		f[son[u]]=f[u]+1, g[son[u]]=g[u]-1;
    		dfs2(son[u],u); 
    	} f[u][0]=1; ans+=g[u][0];
    	for(auto v:e[u]) if(v!=fa&&v!=son[u]) {
    		f[v]=cur, cur+=h[v]*2, g[v]=cur, cur+=h[v]*2;
    		dfs2(v,u);
    		rep(i,0,h[v]-1) ans+=g[u][i+1]*f[v][i], ans%=mod;
    		rep(i,1,h[v]-1) ans+=f[u][i-1]*g[v][i], ans%=mod;
    		rep(i,0,h[v]-1) g[u][i+1]+=f[u][i+1]*f[v][i];
    		rep(i,1,h[v]-1) g[u][i-1]+=g[v][i]; 
    		rep(i,0,h[v]-1) f[u][i+1]+=f[v][i];
    	}
    }
    
    signed main() {
    	n=read();
    	rep(i,2,n) {
    		int u=read(), v=read();
    		e[u].emplace_back(v), e[v].emplace_back(u);
    	}
    	dfs1(1,0), f[1]=cur, cur+=h[1]*2, g[1]=cur, cur+=h[1]*2, dfs2(1,0);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Microsoft Enterprise Library 5.0 系列(二) Cryptography Application Block (初级)
    Microsoft Enterprise Library 5.0 系列(五) Data Access Application Block
    Microsoft Enterprise Library 5.0 系列(八) Unity Dependency Injection and Interception
    Microsoft Enterprise Library 5.0 系列(九) Policy Injection Application Block
    Microsoft Enterprise Library 5.0 系列(三) Validation Application Block (高级)
    软件研发打油诗祝大家节日快乐
    从挖井的故事中想到开发管理中最容易忽视的几个简单道理
    ITIL管理思想的执行工具发布
    管理类软件设计“渔”之演化
    20070926日下午工作流与ITILQQ群 事件管理 讨论聊天记录
  • 原文地址:https://www.cnblogs.com/TetrisCandy/p/15476318.html
Copyright © 2011-2022 走看看