寒武纪-1005 Travel(树形DP)
一、题目链接
http://aiiage.hustoj.com/problem.php?id=1005
二、题面
PDF:http://aiiage.hustoj.com/upload/file/20180114/20180114145400_75397.pdf
三、思路
正赛时,我一开始写了个感觉在$O(N*N*k)$的时间复杂度内做了优化的代码,交上去,TLE。尝试了各种可能的情况,还是TLE。赛后,看了官方题解。如下:
然而,2.3的“相对直观的想法”,实在是没想到一个时间复杂度在$O(N*k*k)$的DP算法。
后来,队友问了其他一位AC的同学,他给我们仔细地讲了如何做DP推导。我算是听明白了。具体如下:
1、改掉题解中$dp$数组的意义。$dp[i][j][k]$表示:在模$k$意义下,到达结点$i$距离为$j$的结点的个数。
2、修改dp的含义后,设置两个$dp$数组:
(1)$dp1[3001][101][101]$。$dp1[i][j][k]$表示在以$i$为根的子树中,在模$k$意义下到达结点$i$的距离为$j$的结点的个数。
(2)$dp[3001][101][101]$。$dp[i][j][k]$表示,在模$k$意义下到达结点$i$距离为$j$的结点的个数。
3、$dp1$数组的递推式不难想到,假设当前结点为$root$:\[dp1[root][j][k] = \sum_i dp1[son_i][j - w_i][k]\]
我们只需要以任意结点$r$为根,使用dfs搜索一遍即可求出所有结点的$dp1$的值。
4、然后,最关键的就是$dp$数组的推导了。如下图所示,要求$dp[x][j][k]$时,还必须知道除以结点$x$为根的子树之外的到结点$x$的距离为$j$(在模$k$意义下)的结点的个数。因为$dp[r][i]][j] = dp1[r][i][j] (0 <= i < j, 2 <= j < k)$,所以,由第一次dfs求出的数组$dp1$,即可知道$dp[r][j][k]$,由$dp[r][j][k]$推导$dp[x][j][k]$的思路是:由于结点$x$的父结点$r$的$dp$数组已知,所以,要求除以结点$x$为根的子树$t$之外的其他结点到结点$x$的距离为$j$(在模$k$意义下)的个数,只需要用$dp[r][j - w][k]$(在模$k$意义下到结点$r$距离为$j-w$的点的个数)减去$dp1[x][j - 2 * w][k]$(到结点$r$的距离为$j-w$,对于子树$t$之外的点,到达结点$x$的距离为$j$;对于子树$t$之内的点,到达结点$x$的距离为$(j - w) - w$。所以是$j - 2 * w$),再加上$dp1[x][j][k]$,即得到了$dp[x][j][k]$。这个地方比较绕,需要结合图示好好理解。
用数学语言表达即为:\[dp[x][j][k] = dp[r][j-w][k] - dp1[x][j - 2 * w][k] + dp1[x][j][k]\]
四、注意事项
1、这题时间和空间都卡得非常紧,注意在dfs的时候,一定要把当前结点下所有$j$和所有$k$的$dp1$和$dp$数组计算完。注意,千万不要在dfs外面套两层循环再传参;这题很好地体现了大量递归调用带来的开销;
2、题解中说,$dp$数组定义为$bool$类型,我想了很久,还是没想到,如果用$bool$类型定义$dp$数组,该如何递推(T_T,说到底还是自己功力不够啊)。如果按照上述思路写代码,$dp$数组的类型设置成$short$最好,因为点数最多就3k个。用$int$肯定会超内存。
3、下面的代码中,第一份是我的,写好后,无论如何优化都是超时。后来加了个编译器-O2优化,1100+ms AC了。第二份是我队友的,不晓得他的代码怎么搞的,感觉思路和代码结构都一样,他的代码不加编译器-O2优化1100+ms AC。加了编译器-O2优化,在加输入挂,300+ms AC。跑的贼快!
4、注意递推式中涉及减法操作,在取模时需要注意。
五、源代码
1、我的
#pragma GCC optimize(2) #pragma comment(linker, "/STACK:102400000, 102400000") #include<bits/stdc++.h> using namespace std; #define MAXN 3001 typedef struct { int to, next, wt; } Edge; Edge tree[MAXN << ]; int head[MAXN], ecnt; ][], dp[MAXN][][]; int N, Q; void init() { memset(head, -, sizeof(head)); ecnt = ; } template <class T> inline void read(T &x) { int t; bool flag = false; ')) ; if(t == '-') flag = true, t = getchar(); x = t - '; + t - '; if(flag) x = -x; } void add(int from, int to, int wt) { tree[ecnt].to = to; tree[ecnt].wt = wt; tree[ecnt].next = head[from]; head[from] = ecnt++; } void dfs0(int root, int par) { int to, i, j, k; ; i = tree[i].next) { to = tree[i].to; if(to != par) { dfs0(to, root); ; j < ; ++j) { ; k <= ; ++k) { dp1[root][j][k] += dp1[to][(j + k - tree[i].wt % k) % k][k]; } } } } } void dfs1(int root, int par) { )memcpy(dp[], dp1[], ])); int to, i, j, k; ; i = tree[i].next) { to = tree[i].to; if(to != par) { ; j < ; ++j) { ; k <= ; ++k) { dp[to][j][k] = dp[root][(j + k - (tree[i].wt % k)) % k][k] - dp1[to][(j + * k - * (tree[i].wt % k)) % k][k] + dp1[to][j][k]; } } dfs1(to, root); } } } int main() { // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); int a, b, c, v, k, i, j, u, m, dis; scanf("%d", &N); init(); ; i < N; ++i) { read(a), read(b), read(c); add(a, b, c); add(b, a, c); } ; u <= N; ++u) { ; m <= ; ++m) { dp1[u][][m] = ); } } dfs0(, -); dfs1(, -); read(Q); while(Q--) { read(v), read(k); ; dis >= ; --dis) { ) { printf("%d\n", dis); break; } } } ; }
2、队友的
#pragma GCC optimize(2) #pragma comment(linker, "/STACK:102400000, 102400000") #include <bits/stdc++.h> using namespace std; ], cnt; struct e { short next, to, v; } edge[]; void add(short a, short b, short c) { edge[++cnt].to = b; edge[cnt].next = head[a]; edge[cnt].v = c; head[a] = cnt; } ][]; ][][]; ][][]; ]; void getfa(short u, short pre) { short w; ; i = edge[i].next) { short v = edge[i].to; if(v == pre) {w = edge[i].v; continue;} fa[v] = u; getfa(v, u); } ) { ; k <= ; k++) { ; j < k; j++) { dp[u][j][k] += dp1[u][j][k]; } } return ; } else { ; k <= ; k++) ; j < k; j++) dp1[pre][j][k] += dp1[u][(j - w % k + k) % k][k]; } } void dfs(short u) { ; i = edge[i].next) { short w = edge[i].v; short v = edge[i].to; if(v == fa[u])continue; ; k <= ; k++) { ; j < k; j++) { dp[v][j][k] = dp1[v][j][k] + dp[u][(j - w % k + k) % k][k] - dp1[v][(j - ( * w) % k + * k) % k][k]; } } dfs(v); } } template <class T> inline void read(T &x) { int t; bool flag = false; ')) ; if(t == '-') flag = true, t = getchar(); x = t - '; + t - '; if(flag) x = -x; } int main() { int n; memset(head, -, sizeof(head)); read(n); int u, v, w; ; i < n; i++) { read(u), read(v), read(w); add(u, v, w); add(v, u, w); } ; u <= n; u++) ; k <= ; k++) dp1[u][][k] = ; getfa(, -); dfs(); ; u <= n; u++) { ; k <= ; k++) { ; j; j--) { if(dp[u][j][k]) { ans[u][k] = j; break; } } } } int q; read(q); while(q--) { read(u), read(w); printf("%d\n", ans[u][w]); } ; }
寒武纪-1005 Travel(树形DP)的更多相关文章
- BZOJ.1576.[Usaco2009 Jan]安全路经Travel(树形DP 并查集)
题目链接 BZOJ 洛谷 先求最短路树.考虑每一条非树边(u,v,len),设w=LCA(u,v),这条边会对w->v上的点x(x!=w)有dis[u]+dis[v]-dis[x]+len的距离 ...
- 2019 Multi-University Training Contest 8 - 1006 - Acesrc and Travel - 树形dp
http://acm.hdu.edu.cn/showproblem.php?pid=6662 仿照 CC B - TREE 那道题的思路写的,差不多.也是要走路径. 像这两种必须走到叶子的路径感觉是必 ...
- 【HDU6662】Acesrc and Travel【树形DP】
题目大意:给你一棵树,每个节点有一个权值,Alice和Bob进行博弈,起点由Alice确定,确定后交替选择下一个点,Alice目标是最终值尽可能大,Bob目标是尽可能小 题解:很明显是树形DP,那么考 ...
- hdu 4612 Warm up 双连通+树形dp思想
Warm up Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others) Total S ...
- hdu4756 Install Air Conditioning(MST + 树形DP)
题目请戳这里 题目大意:给n个点,现在要使这n个点连通,并且要求代价最小.现在有2个点之间不能直接连通(除了第一个点),求最小代价. 题目分析:跟这题一样样的,唉,又是原题..先求mst,然后枚举边, ...
- 树形动态规划(树形DP)入门问题—初探 & 训练
树形DP入门 poj 2342 Anniversary party 先来个题入门一下~ 题意: 某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上 ...
- 算法提高 金属采集_树形dp
算法提高 金属采集 时间限制:1.0s 内存限制:256.0MB 问题描述 人类在火星上发现了一种新的金属!这些金属分布在一些奇怪的地方,不妨叫它节点好了.一些节点之间有道路相连 ...
- HDU 6201 transaction transaction transaction(树形DP)
transaction transaction transaction Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 132768/1 ...
- 没有上司的舞会 树形dp
题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri, ...
随机推荐
- 例子.ZC简单.JSP和session
1.环境: Win7x64.E:\ZC_IDE\Eclipse\Windows\eclipse-jee-mars-R-win32__apk__20180122_1457\eclipse.exe.E:\ ...
- 使用Bootstrap插件datapicker获取时间
引入css和js <link rel="stylesheet" href="/${appName}/commons/css/datapicker/datepicke ...
- flask学习(十三):过滤器
1. 介绍和语法 介绍:过滤器可以处理变量,把原始的变量经过处理后再展示出来,作用的对象是变量
- 也来说说C#异步委托 (转自 Rising_Sun)
前些日子,看到园子里面有人用老王喝茶的例子讲解了一下同步和异步,虽然没有代码实现,但是能够通俗易懂的讲解了同步.异步.阻塞.非阻塞的关系了,今天借题发挥,用一个热水器加热洗澡的例子来具体演示一下C#使 ...
- 1、lambda表达式
lambda表达式中的类型是通过上下文推断出来的,类似String[] strArr = {"as","sd"};右边元素的子类型. 匿名内部类的情况:需要引用 ...
- 今天廷鹏师弟来的java建议
如下一段获取数据代码的问题: public Serializable getById(Serializable id) throws BaseBusinessException { if (id = ...
- icon fonts入门
iconfont网站 http://www.iconfont.cn(推荐) http://fontello.com/ http://fontawesome.io/ https://icomoon. ...
- L173
Technical problems temporarily blocked some US and European users having access to their accounts an ...
- node 使用范围 和 node的优势 (为什么 创业公司 选择的不是 java php ruby 等)
链接 一些国外大公司 范围: 第一: 希望合并后台多个接口 成为一个接口, 或者频繁改动接口 相关, 比如数据 和数据格式之类, 后台难以配合, 这里可以使用node作为后台的应用层调用其他接口 ...
- java 之DelayQueue,TaskDelayed,handlerFactory,dataChange消息配置.收发等.java spring事务处理TransactionTemplate
java 之DelayQueue,TaskDelayed,handlerFactory,dataChange消息配置.收发等.java spring事务处理TransactionTemplate等. ...