ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LG3565 [POI2014]HOT-Hotels 题解

2022-08-31 17:03:02  阅读:160  来源: 互联网

标签:子树 int 题解 POI2014 times HOT len son gets


P3565 [POI2014]HOT-Hotels

给定一棵树,在树上选 \(3\) 个点,要求两两距离相等,求方案数。

原题数据范围 \(n\leq 5000\),可做到线性,空间 \(62.5\text{MB}\)。

sub1

\(n\leq 5000\) 不用多说,直接枚举每一个点作为中点,统计这个点不同的三颗子树中点的选取方案数。定义 \(f_i\) 为先前找到的子树中深度为 \(i\) 的且在不同子树中的点对数,\(g_i\) 为先前找到的子树中深度为 \(i\) 的点的个数,\(bot_i\) 为当前子树中深度为 \(i\) 的点的个数。

每枚举一颗子树,得到转移

\[ans\gets f_i\times bot_i\\ f_i\gets g_i\times bot_i\\ g_i\gets bot_i \]

sub2

不妨换个思路来想问题(指看题解后才懂)。

定义 \(f_{u,i}\) 为 \(u\) 的子树中距离 \(u\) 为 \(i\) 的节点数,\(g_{u,i}\) 为 \(u\) 的子树中的一对的 \(lca\) 到 \(u\) 节点的距离是 \(d-i\) 的方案数(也就是说这对点到 \(lca\) 的距离是 \(d\))。

如图(是自己画的呢)

根据这题的要求 \(dis(x,z)=dis(x,y)=2\times d\),可以得到 \(dis(v,z)=2\times d-(d-j)-d-1=j-1\)。

如果现在加入子树 \(v\) 可以计算出答案:

\[ans\gets f_{v,j-1}\times g_{u,j}+f_{u,j}\times g_{v,j+1}+g_{u,0} \]

同时更新信息

\[g_{u,i}\gets f_{u,i}\times f_{v,i-1}+g_{v,i+1}\\ f_{u,i}\gets f_{v,i-1} \]

每次转移的复杂度是 \(v\) 子树的最大深度,即 \(maxdep_v-dep_u+1\)。

至此我们可以 \(O(n^2)\) 解决这个问题。

sub3

如果 \(u\) 只有一个儿子 \(v\),可以发现 \(f_{u,i}=f_{v,i-1},g_{u,i}=g_{v,i+1}\)。这显然可以直接用指针来继承 \(dp\) 数组。

我们可以使用长链剖分优化这个 \(dp\),长(重)儿子直接指针转移,轻儿子暴力合并即可。因为单次转移的复杂度是树的一条链的长度,这些链不重叠,所以时间复杂度 \(O(n)\)。

代码实现

指针实现的时候为

f[son[u]]=f[u]+1;
g[son[u]]=g[u]-1;

容易 \(g\) 数组儿子节点的初始位置甚至会往前走,所以要 \(g\) 数组的 \(temp\) 数组要开双倍空间,同时预留空间的时候前方也必须预留。

此外所有的题解的转移的 \(u,v\) 和我是相反的,尽管这两者本质相同,我用我的图推出来的转移就是这样的,我也不知道题解是怎么推得如此一致,还是说。。。

const int N=1000006;
vector<int>edge[N];
int n,fat[N],son[N],len[N];//len记录子树内最深的点到这颗子树的根的距离
ll *f[N],tf[N],*pf=tf+1,*g[N],tg[N<<1],*pg=tg+1,ans;
void dfs(int u,int f){
	fat[u]=f;
	for(auto v:edge[u]){
		if(v==f)continue;
		dfs(v,u);
		len[u]=max(len[u],len[v]);
		if(len[v]>len[son[u]])son[u]=v;
	}++len[u];
}
inline void space(int u){
	f[u]=pf,pf+=len[u];
	pg+=len[u];g[u]=pg,pg+=len[u];//g数组的指针会往前走,所以需要在前面预留空间。
}
void dfsp(int u){
	if(son[u]){f[son[u]]=f[u]+1,g[son[u]]=g[u]-1;dfsp(son[u]);}
	f[u][0]=1;ans+=g[u][0];
	for(auto v:edge[u]){
		if(v==fat[u]||v==son[u])continue;
		space(v);dfsp(v);
		for(int i=0;i<=len[v];++i){
			if(i)ans+=f[v][i-1]*g[u][i];
			ans+=g[v][i+1]*f[u][i];
		}
		for(int i=0;i<=len[v];++i){
			if(i)g[u][i]+=f[u][i]*f[v][i-1];
			g[u][i]+=g[v][i+1];
			if(i)f[u][i]+=f[v][i-1];
		}
	}
}
int main(){
	n=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read();
		edge[u].push_back(v);
		edge[v].push_back(u);
	}
	dfs(1,0);
	space(1);dfsp(1);
	printf("%lld\n",ans);
	return 0;
}
// 152ms /  35.19MB /  1.36KB C++14 (GCC 9) O2
//如何评价你谷所有题解的dp转移方向都是一样的。。。

标签:子树,int,题解,POI2014,times,HOT,len,son,gets
来源: https://www.cnblogs.com/BigSmall-En/p/16643683.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有