hdu5593 树dp详解!!!

题目就是给你一个n点的树,每条边权值为1,对于每个点,求出离这个点距离不超过k的点的个数,然后答案是输出每个点的答案的异或和。
分析,首先他是要求每个点距离不超过k的点数,对于每个点,这个不超过距离k的点数,我们可以从下面(就是这个点的子树去找), 也可以从这个点的上方去找。
我们分别去考虑。
用f[i][k] 表示从i这个点往下符合条件(距离为k)的点的答案。
对于每个点,先去得到他子树去找到的所有的点。这个就是很经典的树dp做法了,就是去一遍dfs从叶子节点往上去递归回溯更新答案。最底层就是f[i][0~k] = 1;(i是叶子节点)因为从0到k的距离都是只有他本身。对于非叶子节点,我们要去对于这个节点的每个孩子所能贡献的答案去累加也就是f[i][k] = sum(f[z][k-1]),(z是i的所有孩子节点),k-1是因为从该节点往下面的孩子去需要花费1的价值。
我们得到了f数组后。接下来就考虑对于每一个点往上所能得到的最多答案。
怎么分析呢?
我们设g[i][k]表示i这个节点往上(也就从他的父亲)能延伸得到的符合条件(距离为k)的点的答案
如果我们对于一个节点,找到他父亲的节点往上所能延伸的答案,这样就能得到该节点的答案吗?

我们从5这个节点来看,如果要得到g[5][k](节点5往上去延伸长度为k的点的个数),如果只是去找他父亲(节点2)的g[2][k-1],那么我们会算漏,算漏的部分就是节点5的父亲节点2的另外的孩子就是2这个节点去往下(不算5)也可以有点是满足的。要怎么得到呢?就是用我们之前找到的f[2][k-1]的总和去减去f[5][k-2]的值就是所有(2节点往下)符合的点的答案,(如果不理解可以手写写看,简单的加减问题);
所以得到了一个递推式子也就是图中的式子。
如果写呢?就是在跑一次dfs,从上往下更新g数组就可以了。
这样子就能把所有点的答案算出来了。
对于每个点的答案就是g[i][k]+f[i][k]-1;(这个减1还是不减1因人而异,就看你的g有没有多算一遍节点本身。
然后我的程序是所有的g,f数组,都算了所有的1到k的答案,但是从递推式很明显可以看出其实可以优化的不用算那么多。(最多需要算的就只是k-2到k这个区间的就可以了)。
(小坑点!算点的时候要用long long !!!!);
如果有问题可以留言,我会尽力解答。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#define ll long long
using namespace std;
const long long max_ = 500000 + 500;
inline int read() {
	int s = 0, f = 1;
	char ch = getchar();
	while (ch<'0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0'&&ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}
int A, B,n,k,xiann = 1,head[max_];
inline int father_(int i) {
//	return (A*i + B) % (i - 1) + 1;
	return ((A % (i - 1)) *(ll) i % (i - 1) +B % (i - 1)) % (i - 1) + 1;
}
struct v {
	int next, to, value;
}xian[2*max_];
inline void add_(int a, int b, int c) {
	xian[xiann].next = head[a];
	xian[xiann].to = b;
	xian[xiann].value = c;
	head[a] = xiann;
	xiann++;
}
int f[max_][15],g[max_][15];
void dfs(int now, int fa) {
	for (int i = 0; i <= k; i++) {
		f[now][i] = 1;
	}
	for (int i = head[now]; i; i = xian[i].next) {
		if (xian[i].to != fa) {
			dfs(xian[i].to, now);
		}
	}
	for (int z = 1; z <= k ; z++) {
		for (int i = head[now]; i; i = xian[i].next) {
			if(xian[i].to!=fa)
			f[now][z] += f[xian[i].to][z - 1];
		}
	}
}
void chushihua() {
	for (int i = 0; i <= k; i++) {
		g[1][i] =1;
	}
	for (int i = 2; i <= n; i++) {
		g[i][0] = 1;
		g[i][1] = 2;
	}
}
void dfs2(int now, int fa) {
	for (int i = head[now]; i; i = xian[i].next) {
		if (xian[i].to != fa) {
			for (int z = 2; z <= k; z++) {
				g[xian[i].to][z] = g[now][z - 1] + f[now][z - 1] - f[xian[i].to][z - 2];
			}
		}
	}
	for (int i = head[now]; i; i = xian[i].next) {
		if (xian[i].to != fa) {
			dfs2(xian[i].to, now);
		}
	}
}
void qing() {
	xiann = 1;
	for (int i = 0; i <= n+1; i++) {
		head[i] = xian[i].next = xian[i].to = xian[i].value = 0;
		for (int j = 0; j <= k + 1; j++) {
			f[i][j] = 0; g[i][j] = 0;
		}
	}
}
int main()	 {
	int T = read();
	while (T--) {
		qing();
		n = read(); k = read(); A = read(); B = read();
		//1做根就可以
		for (int i = 2; i <= n; i++) {
			int t = father_(i);
			add_(t, i, 1);
			add_(i, t, 1);
		}
		dfs(1, 0);
		chushihua();
		dfs2(1, 0);
		int ans = f[1][k] ;
		for (int i = 2; i <= n; i++) {
			ans = ans ^ (f[i][k] + g[i][k] - 1);
		}
		cout << ans<<endl;	
	}
	return 0;
}
全部评论

相关推荐

代码飞升_不回私信人...:啊喂笨蛋算法为什么写查找,线程池怎么放计网上去了,写动态规划真的不会被狠狠地制裁吗oi
点赞 评论 收藏
分享
头像
10-13 18:10
已编辑
东南大学 C++
。收拾收拾心情下一家吧————————————————10.12更新上面不知道怎么的,每次在手机上编辑都会只有最后一行才会显示。原本不想写凉经的,太伤感情了,但过了一天想了想,凉经的拿起来好好整理,就像象棋一样,你进步最快的时候不是你赢棋的时候,而是在输棋的时候。那废话不多说,就做个复盘吧。一面:1,经典自我介绍2,项目盘问,没啥好说的,感觉问的不是很多3,八股问的比较奇怪,他会深挖性地问一些,比如,我知道MMU,那你知不知道QMMU(记得是这个,总之就是MMU前面加一个字母)4,知不知道slab内存分配器-&gt;这个我清楚5,知不知道排序算法,排序算法一般怎么用6,写一道力扣的,最长回文子串反问:1,工作内容2,工作强度3,关于友商的问题-&gt;后面这个问题问HR去了,和中兴有关,数通这个行业和友商相关的不要提,这个行业和别的行业不同,别的行业干同一行的都是竞争关系,数通这个行业的不同企业的关系比较微妙。特别细节的问题我确实不知道,但一面没挂我。接下来是我被挂的二面,先说说我挂在哪里,技术性问题我应该没啥问题,主要是一些解决问题思路上的回答,一方面是这方面我准备的不多,另一方面是这个面试写的是“专业面试二面”,但是感觉问的问题都是一些主管面/综合面才会问的问题,就是不问技术问方法论。我以前形成的思维定式就是专业面会就是会,不会就直说不会,但事实上如果问到方法论性质的问题的话得扯一下皮,不能按照上面这个模式。刚到位置上就看到面试官叹了一口气,有一些不详的预感。我是下午1点45左右面的。1,经典自我介绍2,你是怎么完成这个项目的,分成几个步骤。我大致说了一下。你有没有觉得你的步骤里面缺了一些什么,(这里已经在引导我往他想的那个方向走了),比如你一个人的能力永远是不够的,,,我们平时会有一些组内的会议来沟通我们的所思所想。。。。3,你在项目中遇到的最困难的地方在什么方面4,说一下你知道的TCP/IP协议网络模型中的网络层有关的协议......5,接着4问,你觉得现在的socket有什么样的缺点,有什么样的优化方向?6,中间手撕了一道很简单的快慢指针的问题。大概是在链表的倒数第N个位置插入一个节点。————————————————————————————————————10.13晚更新补充一下一面说的一些奇怪的概念:1,提到了RPC2,提到了fu(第四声)拷贝,我当时说我只知道零拷贝,知道mmap,然后他说mmap是其中的一种方式,然后他问我知不知道DPDK,我说不知道,他说这个是一个高性能的拷贝方式3,MMU这个前面加了一个什么字母我这里没记,别问我了4,后面还提到了LTU,VFIO,孩子真的不会。
走呀走:华子二面可能会有场景题的,是有些开放性的问题了
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务