hdu 6035:Colorful Tree (2017 多校第一场 1003) 【树形dp】
题目链接
单独考虑每一种颜色,答案就是对于每种颜色至少经过一次这种的路径条数之和。反过来思考只需要求有多少条路径没有经过这种颜色即可。
具体实现过程比较复杂,很神奇的一个树形dp,下面给出一个含较详细注释的代码及对应的一组自造的数据以及图片来进行解释
欢迎交流,给出意见~~~
数据
/*第二行的1 2 3在图中分别用红黄蓝来表示 15 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 1 3 2 4 2 5 3 6 3 7 4 8 4 9 5 10 5 11 6 12 6 13 7 14 7 15 */
含注释的代码
#include<bits/stdc++.h> using namespace std; typedef long long LL;const int N=2e5+5; int n; int c[N]; //各结点的颜色 int size[N]; //size[i]记录结点i的子树的大小,叶子为1 int vis[N]; //vis[i]记录颜色i是否出现过 int sum[N]; //见函数内注释及图片 vector<int> adj[N]; LL ans,de;LL C(LL a,LL b) //计算组合数,这样写是为了增加后面的代码的可读性 {LL ret=1;for(int i=1;i<=b;i++)ret=ret*(a+1-i)/i;return ret; }void dfs(int u,int pre) {// 定义 u 为“当前结点” // sum[c[u]]表示, 整棵树中,已经被dfs过的, // 与当前结点具有相同颜色的结点的所有子树的大小之和// 不重复计数// 图一展示了的是刚刚进入dfs(7,3)时sum[c[u]]包含的结点 printf("\n\n==================================\nEnter->%d\n\n",u);int all=0;size[u]=1;for(int to:adj[u]){if(to==pre) continue;int sumu_bd=sum[c[u]]; //bd : before dfs//记录此次dfs前的sum[c[u]] dfs(to,u);size[u]+=size[to];int part=sum[c[u]]-sumu_bd; //dfs过后,sum[c[u]]会产生一个增量,用part来记录这个增量 all+=part; //all用来记录,对u dfs的过程中,sum[c[u]]产生的总增量//这是在为 u返回pre时更新sum[c[u]]做准备 printf("from %d return to %d\n",to,u);printf("all=%d,sumu_bd=%d,part=%d,sum[c[u]]=%d\n",all,sumu_bd,part,sum[c[u]]); printf("to=%d,size[to]=%d\n",to,size[to]);de+=C(size[to]-part,2); //size[to]-part的含义: 在to为根节点的子树中,有一部分结点与to位于同一“块”,// 这个块以 与u颜色相同的结点(不含),或是叶子(含,若叶子与u颜色相同则不含) 为边界// size[to]-part表示的是这个块的大小// 图二展示了 dfs(1,0)内,刚刚执行完dfs(2,1)后,更新de时的size[2]-part printf("\nnow,de=%d\n\n",de);}printf("\n\n=== leaving... ===\n\n");printf("pre_sum[c[u]]=%d\n",sum[c[u]]);sum[c[u]]+=size[u]-all;printf("after_sum[c[u]]=%d\n",sum[c[u]]);printf("\nleave from %d\n\n==================================\n\n",u); }int main() {int kase=0;while(~scanf("%d",&n)){memset(vis,0,sizeof(vis));memset(size,0,sizeof(size));memset(sum,0,sizeof(sum));for(int i=0;i<=n;i++) adj[i].clear();int c_num=0;de=0;for(int i=1;i<=n;i++){scanf("%d",&c[i]);if(!vis[c[i]]) c_num++;vis[c[i]]=1;}for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);adj[u].push_back(v);adj[v].push_back(u);}dfs(1,0);for(int i=1;i<=n;i++)if(i!=c[1]&&vis[i]) de+=C(n-sum[i],2);ans=C(n,2)*c_num-de;printf("Case #%d: %lld\n",++kase,ans);} }
图一:
图二:
AC代码
#include<bits/stdc++.h> using namespace std; typedef long long LL;const int N=2e5+5; int n; int c[N]; int size[N]; int vis[N]; int sum[N]; vector<int> adj[N]; LL ans,de;LL C(LL a,LL b) {LL ret=1;for(int i=1;i<=b;i++)ret=ret*(a+1-i)/i;return ret; }void dfs(int u,int pre) {int all=0;size[u]=1;for(int to:adj[u]){if(to==pre) continue;int sumu_bd=sum[c[u]];dfs(to,u);size[u]+=size[to];int part=sum[c[u]]-sumu_bd;all+=part;de+=C(size[to]-part,2);}sum[c[u]]+=size[u]-all; }int main() {int kase=0;while(~scanf("%d",&n)){memset(vis,0,sizeof(vis));memset(size,0,sizeof(size));memset(sum,0,sizeof(sum));for(int i=0;i<=n;i++) adj[i].clear();int c_num=0;de=0;for(int i=1;i<=n;i++){scanf("%d",&c[i]);if(!vis[c[i]]) c_num++;vis[c[i]]=1;}for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);adj[u].push_back(v);adj[v].push_back(u);}dfs(1,0);for(int i=1;i<=n;i++)if(i!=c[1]&&vis[i]) de+=C(n-sum[i],2);ans=C(n,2)*c_num-de;printf("Case #%d: %lld\n",++kase,ans);} }
简版的
转载于:https://www.cnblogs.com/Just--Do--It/p/7239484.html
hdu 6035:Colorful Tree (2017 多校第一场 1003) 【树形dp】相关推荐
- HDU 6035 Colorful Tree(补集思想+树形DP)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6035 [题目大意] 给出一颗树,一条路径的价值为其上点权的种类数,求路径总价值 [题解] 单独考虑 ...
- 2019牛客多校第一场
2019牛客多校第一场 题号 题目 知识点 A Monotonic Matrix B Symmetric Matrix C Fluorescent 2 D Two Graphs E Removal F ...
- HDU6578 2019HDU多校训练赛第一场 1001 (dp)
HDU6578 2019HDU多校训练赛第一场 1001 (dp) 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6578 题意: 你有n个空需要去填,有 ...
- 2020 牛客多校第一场
2020 牛客多校第一场 A. B-Suffix Array 后缀数组的思想:倍增+桶排序的方式找出一串连续序列后缀的大小.虽说正常使用的时候都是字典序,但是只要修改排序方式,也能够达到一个类似的&q ...
- 2019年杭电多校第一场 1001题blank(DP)HDU6578
2019年杭电多校第一场 1001题blank(DP)HDU6578 解决思路,开一个DP数组来存储0 1 2 3四个字符最后出现的位置,并且在DP中已经==排好序==. DP开四维,DP[i][j] ...
- 2019HDU多校第一场1001 BLANK (DP)(HDU6578)
2019HDU多校第一场1001 BLANK (DP) 题意:构造一个长度为n(n<=10)的序列,其中的值域为{0,1,2,3}存在m个限制条件,表示为 l r x意义为[L,R]区间里最多能 ...
- python字符串去重及排序 牛客_2018牛客多校第一场 D.Two Graphs
题意: n个点,m1条边的图E1,n个点,m2条边的图E2.求图E2有多少子图跟图E1同构. 题解: 用STL的全排列函数next_permutation()枚举映射.对于每一种映射枚举每一条边判断合 ...
- 【多校训练】2021牛客多校第一场
[前言] 组队训练的第一场比赛,感觉这场出题十分阴间,后面几个乱搞题根本不会.jpg 赛时只过了5题,rk123,学校参加5/8. A. Alice and Bob [题意] 两人博弈,每次一个人从一 ...
- 【2019多校第一场补题 / HDU6578】2019多校第一场A题1001Blank——dp
HDU6578链接 题意 有一串字符串,仅由 {0,1,2,3}\{0, 1, 2, 3\}{0,1,2,3} 组成,长度为 nnn,同时满足 mmm 个条件.每个条件由三个整数组成:l.r.xl.r ...
最新文章
- 经常使用的npm命令
- spring mvc + mybatis 框架搭建 ( idea + gradle)
- Python-Django配置阿里大于的短信验证码接口
- 李伯虎院士:新一代人工智能引领下的智造制造初步实践
- 在vue-cli创建的项目里配置scss
- C语言 链表的创建--打印--逆置--新增--删除--排序--释放
- (转)ubuntu 文件系统
- 狄克斯特拉算法(入门)
- 欧拉函数的性质及其证明
- 自定义异常和经验小结
- Mysql常用分组聚合函数(统计行的数量、最大值、最小值、平均值、求和)
- ++[[]][+[]]+[+[]] == 10 //true
- 小明用计算机算出58十35,人教版三年级数学下册第三四单元测试卷.docx
- 人脸识别系统_设计说明书
- Android中Toast的简单使用
- 单机游戏计时器防zuo弊解决方案
- 每天学一点 mysql
- win10锁屏后默认1分钟进入睡眠状态的解决办法
- 商品-商品订单-支付订单
- openstack-创建多网络虚机