51nod 1588 幸运树

来源:互联网 发布:项目建议书软件 编辑:程序博客网 时间:2024/05/02 02:14

51nod 1588 幸运树

codeforces lucky tree


这一题第一眼看去就认为是树形dp,然后推出公式,时间复杂度O(n)*check lucky的代价,最多9。

并且可以对公式化简合并,得到简洁的形式。然后就没继续想下去了,直接套模板了。


然后才发现解题报告里有另一种方法,考虑了一下,该方法才更接近问题的本质,将问题转化为遍历联通块了,即可得到最终答案。


两种方法的时间复杂度是相同的,其实公式也是一样的。   但还是觉得第二种方法更胜一筹,又简洁明了,浅显易懂,易推导,更重要的是更加剖析到了问题的本质


欲哭无泪,脑袋真的是僵化了。以下是我的第一种方法树形dp的代码,直接套以前的模板修改一下就AC了。   

代码里还要建树得到关系,还要从下往上一次dp,再从上往下一次dp,唉,尽管复杂度一样都是线性,但就是麻烦,被打击到了...



#include<iostream>using namespace std;#define maxlen 200001#define result_len 2//int s[maxlen];int *c[maxlen];//[5]bool *is_valid[maxlen];int x[maxlen], y[maxlen];int myarray[maxlen];//int father[maxlen];bool is_visited[maxlen];int result[maxlen];int tree_size[maxlen];int weight[maxlen];int friend_num[maxlen];//int ans[maxlen][maxlen1][3];int n, root, tmp;__int64 myresult = 0;inline bool check(int &x){do {tmp = x % 10;x /= 10;if (tmp != 4 && tmp != 7){return false;}} while (x);return true;}void getInput(){scanf("%d", &n);/*int x, y;for (int i = 1; i < n; ++i){scanf("%d%d", &x, &y);c[x][++c[x][0]] = y;c[y][++c[y][0]] = x;}*/for (int i = 1; i < n; ++i){scanf("%d%d%d", &x[i], &y[i], &weight[i]);++friend_num[x[i]];++friend_num[y[i]];}for (int i = 1; i <= n; ++i){c[i] = new int[friend_num[i] + 1];is_valid[i] = new bool[friend_num[i] + 1];c[i][0] = 0 ;    //result[i][0] = tree_size[i] }for (int i = 1; i < n; ++i){c[x[i]][++c[x[i]][0]] = y[i];c[y[i]][++c[y[i]][0]] = x[i];is_valid[x[i]][c[x[i]][0]] = is_valid[y[i]][c[y[i]][0]] = check(weight[i]);}}inline void dp1(int &x){int father_index = c[x][c[x][0]];if (is_valid[x][c[x][0]]){//result[x][1] = n - tree_size[x];result[x] += n - tree_size[x];}else{//result[x][1] = result[father_index][1] + result[father_index][0] - result[x][0];result[x] = result[father_index];}myresult += __int64(result[x]) * (result[x] - 1);//printf("%d\t%d\t%d\t%d\t%lld\n", x, result[x][0], result[x][1], tree_size[x], myresult);}inline void dp(int &x){int father_index = c[x][c[x][0]];tree_size[father_index] += ++tree_size[x];if (is_valid[x][c[x][0]]){result[father_index] += tree_size[x];}else{result[father_index] += result[x];}}//建立树,时间复杂度O(n),n为树节点个数。//myarray数组存储的是拓扑排序过程的节点序列,可以后续利用//若是根节点可能为0或负数,要用另一种写法void buildTree(){const int father_num = 1, root_father = 0; //root_father也可以设为n+1//找根root的过程,可以自己设定root = 1;int num = 0;for (int i = 2; i <= n; ++i){if (1 == c[i][0]){myarray[num++] = i;is_visited[i] = true;}/*else if (c[i][0] > c[root][0])//把孩子最多的节点作为root{root = i;}*/}/*if (0 == root){root = myarray[--num];is_visited[root] = false;}*/++friend_num[root];c[root][++c[root][0]] = root_father;//为了计算方便,将0作为根节点的父节点is_valid[root][c[root][0]] = true;//利用叶子节点度为1的思想,进行拓扑排序,建立树,最终c[i][0]存储 度、边数,  1-c[i][0]为所有相邻点, 最后一个c[i][c[i][0]]为父节点//根节点的父节点为0,也可以自己设定。int index, tmp_father, index_begin = 0;while (index_begin < num){index = myarray[index_begin++];for (int i = 1; i <= c[index][0]; ++i){if (!is_visited[tmp_father = c[index][i]]){if ((--friend_num[tmp_father]) == father_num)//删减index,若之后父节点入度为0,则入队列{myarray[num++] = tmp_father;is_visited[tmp_father] = true;}tmp = is_valid[index][c[index][0]];is_valid[index][c[index][0]] = is_valid[index][i];is_valid[index][i] = tmp;c[index][i] = c[index][c[index][0]];//father移至最后c[index][c[index][0]] = tmp_father;break;}}dp(index);}//从根节点开始递推for (int i = num - 1; i >= 0; --i){dp1(myarray[i]);}}void test(){printf("%lld\n", myresult);}int main(){getInput();buildTree();test();return 0;}


0 0
原创粉丝点击