bzoj 3696 化合物 树形dp (附复杂度证明!!!)

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=2318

题目大意

介于这是一道权限题,先讲一下题意
有一棵根节点编号为1的数,给出每一个节点的父亲。
对于点对(x,y),令他们的LCA为k,定义这对点对的A值为dis[x][k])^dis[y][k],dis即为两点间的最短距离(边数),
最后求出对于x=(1…n),A值为x的点对的数量。
点数1e5,且题目保证最大的深度为500

前言

看到这道题马上想到树形dp,但是冷静下来分析一下,发现复杂度不太对,写了一下发现跑的飞快,百度了一下这道题的题解,连hzwer大佬都说复杂度不会分析,不过在大佬的帮助下我得知了如何证明复杂度,这里想和大家分享一下。

正文

最暴力的做法就是枚举两个点,算一下lca,两个深度异或一下加到答案里,复杂度 n 2 ∗ l o g n^2*log n2log,这个应该都没问题,不细讲了。
再一步想到枚举lca

void dfs(int u){
	f[u][0]=1; //我们以f[i][j]表示到i这个点距离为j的点的数量,当然指考虑在i下方的点,初始f[u][0]=1
	for (int p=head[u];p;p=nxt[p]){
		int v=son[p];
		dfs(v);
		for (int d=0;d<=maxdep[u];d++){//相当于枚举u之前的子树
			for (int i=0;i<=maxdep[v];i++){//枚举v中的点
				ans[d^(i+1)]+=1ll*f[u][d]*f[v][i];//转移
			}
		}
		for (int d=0;d<=maxdep[v];d++) f[u][d+1]+=f[v][d];//将v这颗子树加进来
		maxdep[u]=max(maxdep[u],maxdep[v]+1);//更新一下最大深度
	}
}

粗略的看,两个枚举深度应该是 d e p 2 dep^2 dep2,所以总复杂度是 n ∗ d e p 2 n*dep^2 ndep2,如果是满的话肯定跑不过,不信你可以把maxdep直接改成500。
所以每次只更新到maxdep这么神奇?
由于我们每个子树中深度相同的点都并到了一起,所以我们每个子树合并时可以看成两条链合并,当短的这条链合并到长的这条链时直接被吃掉了,因为它被合并到了一个和他深度相同的点上去了。bzoj 3696 化合物 树形dp (附复杂度证明!!!)_第1张图片

例如这边的这个红点,当他与这条绿链合并时,被这个蓝点吃掉了,所以当我们在把左边这个子树与黄链合并的时候,红点和蓝点会同时和左边形成贡献,所以可以看做红点被蓝点吃掉了。
那么我们再来考虑总贡献次数,每个点都会在被吃掉前与吃掉它的那一条链上的每个点形成以的贡献,所以总复杂度就是点数 ∗ * 最长链的长度了,即 n ∗ m a x d e p n*maxdep nmaxdep,所以树形dp事实上的复杂度是正确的。

结语

这道树形dp本身不难,但是这种通过每个点的贡献来分析复杂度的方法还是很值得学习的

你可能感兴趣的:(bzoj)