一、题目链接

  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)的更多相关文章

  1. 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的距离 ...

  2. 2019 Multi-University Training Contest 8 - 1006 - Acesrc and Travel - 树形dp

    http://acm.hdu.edu.cn/showproblem.php?pid=6662 仿照 CC B - TREE 那道题的思路写的,差不多.也是要走路径. 像这两种必须走到叶子的路径感觉是必 ...

  3. 【HDU6662】Acesrc and Travel【树形DP】

    题目大意:给你一棵树,每个节点有一个权值,Alice和Bob进行博弈,起点由Alice确定,确定后交替选择下一个点,Alice目标是最终值尽可能大,Bob目标是尽可能小 题解:很明显是树形DP,那么考 ...

  4. hdu 4612 Warm up 双连通+树形dp思想

    Warm up Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total S ...

  5. hdu4756 Install Air Conditioning(MST + 树形DP)

    题目请戳这里 题目大意:给n个点,现在要使这n个点连通,并且要求代价最小.现在有2个点之间不能直接连通(除了第一个点),求最小代价. 题目分析:跟这题一样样的,唉,又是原题..先求mst,然后枚举边, ...

  6. 树形动态规划(树形DP)入门问题—初探 & 训练

    树形DP入门 poj 2342 Anniversary party   先来个题入门一下~ 题意: 某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上 ...

  7. 算法提高 金属采集_树形dp

    算法提高 金属采集   时间限制:1.0s   内存限制:256.0MB        问题描述 人类在火星上发现了一种新的金属!这些金属分布在一些奇怪的地方,不妨叫它节点好了.一些节点之间有道路相连 ...

  8. HDU 6201 transaction transaction transaction(树形DP)

    transaction transaction transaction Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 132768/1 ...

  9. 没有上司的舞会 树形dp

    题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri, ...

随机推荐

  1. mysql导入source数据库

    首先要确保数据库存在,如果不存在则创建 方法1 source 很智能,很方便,很快捷. # mysql -uroot -p Enter password: Welcome to the MySQL m ...

  2. vs_u8前缀

    1.ZC: 个人测试下来,VS2015开始 支持 u8前缀. 2.What's New for Visual C++ in Visual Studio 2015 https://msdn.micros ...

  3. Java Spring-AOP中的动态代理

    2017-11-10 16:17:12 AOP中有两种代理方式,分别是JDK的动态代理和CGLib的动态代理. JDK的动态代理 Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创 ...

  4. js获取当前日期加上30天之后的日期

    var date1 = new Date(); var date2 = new Date(date1); date2.setDate(date1.getDate() + 30); console.lo ...

  5. JS代码执行机制

    JS代码从编译到执行 我们写出一段JS代码,JS的引擎并不是按照我们书写的顺序从上到下顺序编译并且执行的,首先是按照自己的规则对我们的代码先进行编译,然后从上到下执行编译的代码. 在全局作用域中,JS ...

  6. web 开发 css 默认值列表

    css默认值列表 HTML标签CSS属性默认值汇总 这个东西,在你需要还原默认值的时候,比较有用. 开始的时候 *{margin:0;padding:0;},当需要使用边距的时候,就需要还原HTML默 ...

  7. 一起来点React Native——你必须要会点FlexBox布局

    一.FlexBox布局 1.1 FlexBox是什么意思呢? flexible(形容词):能够伸缩或者很容易变化,以适应外界条件的变化 box(名词):通用的矩形容器 1.2  什么是FlexBox布 ...

  8. 基于Html5的爱情主题网站–表白神器

    介绍 一个基于基于Html5的爱情主题,文字采用打字机效果,逐字打印,并带有键盘敲击声音.在chrome,safari,firefox,IE10下都有效,chrome下效果最佳.要注意的是safari ...

  9. scikit-learn 学习笔记-- Generalized Linear Models (二)

    Lasso regression 今天介绍另外一种带正则项的线性回归, ridge regression 的正则项是二范数,还有另外一种是一范数的,也就是lasso 回归,lasso 回归的正则项是系 ...

  10. Dijkstra算法(C语言)

    Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Di ...