我的树形dp果然是渣。。。

题意:给一棵树,共n(0<n<=15e4)个节点,可在树上进行跳跃,每次跳的最大距离为k(0<k<=5),定义f(s,t)为(dis(s,t)+k)/k,问Σf(s,t),s<t。

解题思路:

  显然是树形dp,问题在于怎么构建状态。

  最简单想到的就是,每到一个节点u,记录其子树中与其距离为d的的节点的数目,即(dis,cnt)对,则答案分两种情况,u到其子节点和以及子节点经过u到子节点,问题变得很简单,计算也不难——但问题在于极端情况下——比如树退化成链,时空复杂度都将高到无法忍受,于是卡在了这里……

  然后看了别人的题解,发现不需要构建(dis,cnt)对,而是构建(dis%k,cnt)对——这样复杂度枚举的最高复杂度也就只是k^2而不是dis^2,,,啊感觉自己宛若一个zz。

  定义,sz(u,i)表示与节点u的距离%k为 i 的节点数目,dp(u,i)表示从u到sz(u,i)中节点的跳数和。

  状态转移如下:

  (1)sz(u,(i+1)%k)+=sz(v,i)

  (2)dp(u,(i+1)%k)+=dp(v,i)  0<i<k

  (3)dp(u,1%k)+=dp(v,0)+sz(v,0)

  显然,与u的子节点v的距离%k大于0的,到v所需跳数与到u所需跳数是相同的(式子(2))。否则跳数需要+1,有sz(v,0)个点,故再加上sz(v,0)(式子(3))。

  接下来是统计答案,分两种情况,一个是u到其子节点,另一个是u到子节点到其它子节点(子节点间的最近公共祖先节点为u)。

  对于第一种,直接res+=Σdp[u][i]即可。

  对于第二种,将之前已经遍历过的子树节点都合并到dp[u][i]与sz[u][i]中,则与新的子树节点v合并时,枚举k1、k2,依次为已遍历过的子树节点与u的距离%k=k1,以及与v的子树中与u的距离%k为k2的节点(语文不好这段贼拗口……k^2的枚举就是这里了),分情况讨论:

  如果k1为0,则dp[u][k1]中则为恰好到达u的跳数(即每一跳距离都是k),则从sz[u][k1]中的节点到达 v的子树中的节点 所需跳数 恰好为二者相加之和,不需额外处理;如果k2为0,同理;当k1与k2都不为0时,考虑k1+k2<=k,则此时说明多计算了一跳,因此需要减去;当k1+k2>k时,恰好符合。

  综上,需要特判的也只有(k1&&k2&&k1+k2<=k)这种情况。

  代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
#define sqr(x) ((x)*(x))
const int N=2e5+;
int head[N],nxt[N<<],to[N<<],cnt;
int n,k,a,b;
ll sz[N][],dp[N][],res;
void init(){
memset(head,-,sizeof(head));
res=cnt=;
memset(sz,,sizeof(sz));
memset(dp,,sizeof(dp));
}
void addEdge(int u,int v){
nxt[cnt]=head[u];
to[cnt]=v;
head[u]=cnt++;
}
void dfs(int u,int pre){
ll tsz[],tdp[]; //用以暂时保存从u到新的v子树节点的数据
for(int e=head[u];~e;e=nxt[e]){
int v=to[e];
if(v==pre) continue;
dfs(v,u);
memset(tsz,,sizeof(tsz));
memset(tdp,,sizeof(tdp));
for(int i=;i<k;i++)
tsz[(i+)%k]+=sz[v][i];
for(int i=;i<k;i++)
tdp[(i+)%k]+=dp[v][i];
tdp[%k]+=dp[v][]+sz[v][];
for(int k1=;k1<k;k1++){
for(int k2=;k2<k;k2++){
res+=dp[u][k1]*tsz[k2]+sz[u][k1]*tdp[k2];
if(k1&&k2&&k1+k2<=k) res-=sz[u][k1]*tsz[k2];
}
}
//将v的子树节点情况合并到u下
for(int i=;i<k;i++)
dp[u][i]+=tdp[i],sz[u][i]+=tsz[i];
}
for(int i=;i<k;i++)
res+=dp[u][i];
sz[u][]++;
}
int main(){
//freopen("in.txt","r",stdin);
while(~scanf("%d%d",&n,&k)){
init();
for(int i=;i<n;i++){
scanf("%d%d",&a,&b);
addEdge(a,b);
addEdge(b,a);
}
dfs(,);
printf("%I64d\n",res);
}
return ;
}

  参考题解:http://www.cnblogs.com/AOQNRMGYXLMV/p/6579771.html

Codeforces 771C的更多相关文章

  1. CodeForces 771C Bear and Tree Jumps 树形DP

    题意: 给出一棵树,一个人可以在树上跳,每次最多跳\(k(1 \leq k \leq 5)\)个点 定义\(f(s,t)\)为从顶点\(s\)跳到顶点\(t\)最少需要跳多少次 求\(\sum\lim ...

  2. python爬虫学习(5) —— 扒一下codeforces题面

    上一次我们拿学校的URP做了个小小的demo.... 其实我们还可以把每个学生的证件照爬下来做成一个证件照校花校草评比 另外也可以写一个物理实验自动选课... 但是出于多种原因,,还是绕开这些敏感话题 ...

  3. 【Codeforces 738D】Sea Battle(贪心)

    http://codeforces.com/contest/738/problem/D Galya is playing one-dimensional Sea Battle on a 1 × n g ...

  4. 【Codeforces 738C】Road to Cinema

    http://codeforces.com/contest/738/problem/C Vasya is currently at a car rental service, and he wants ...

  5. 【Codeforces 738A】Interview with Oleg

    http://codeforces.com/contest/738/problem/A Polycarp has interviewed Oleg and has written the interv ...

  6. CodeForces - 662A Gambling Nim

    http://codeforces.com/problemset/problem/662/A 题目大意: 给定n(n <= 500000)张卡片,每张卡片的两个面都写有数字,每个面都有0.5的概 ...

  7. CodeForces - 274B Zero Tree

    http://codeforces.com/problemset/problem/274/B 题目大意: 给定你一颗树,每个点上有权值. 现在你每次取出这颗树的一颗子树(即点集和边集均是原图的子集的连 ...

  8. CodeForces - 261B Maxim and Restaurant

    http://codeforces.com/problemset/problem/261/B 题目大意:给定n个数a1-an(n<=50,ai<=50),随机打乱后,记Si=a1+a2+a ...

  9. CodeForces - 696B Puzzles

    http://codeforces.com/problemset/problem/696/B 题目大意: 这是一颗有n个点的树,你从根开始游走,每当你第一次到达一个点时,把这个点的权记为(你已经到过不 ...

随机推荐

  1. Python supprocess模块

    当我们需要调用系统的命令的时候,最先考虑的os模块.用os.system()和os.popen()来进行操作.但是这两个命令过于简单,不能完成一些复杂的操作,如给运行的命令提供输入或者读取命令的输出, ...

  2. 洛谷 1937 [USACO10MAR]仓配置Barn Allocation

    [题解] 贪心. 把区间按照右端点从小到大排序,右端点相同的按照长度从小到大排序,然后按顺序考虑,能放就放下去. 维护能不能放下去用线段树即可. #include<cstdio> #inc ...

  3. BZOJ 3916 [Baltic2014]friends

    [题解] 暴力枚举添加字符的位置,然后判断去掉这个位置的字符串是否有由两个相同的串拼接而成.本题的n的范围为200W,暴力判断会TLE,所以要使用哈希. #include<cstdio> ...

  4. Python学习笔记 (1)Hello World(环境搭建+输出Hello World!)

    随想 高考发挥失常.科三遇火车发挥失常,各种不顺……突然发现假期都快没了,才想起高考前想象的这个假期要做的一堆事,现在来多完成一件吧. 这几篇博客仅只是我的学习笔记,凑合看吧.我这个python小白看 ...

  5. tmux使用入门

    tmux是Linux中窗口管理程序,适用于终端复用,尤其适合远程连接.最近,我正苦闷与ssh自动超时退出和broken pipe,决定投入tmux怀抱. 使用tmux最直接的好处,便是可以在一个远程连 ...

  6. GUI 总结(一)

    一/概述 1.两个包: javax.awt //before java 1.2 javax.swing //after java 1.2 2.两个词: 组件Component 容器Container ...

  7. Nth Largest Value

    Problem Description For this problem, you will write a program that prints the Nth largest value in ...

  8. [bzoj4987]Tree_树形dp

    Tree bzoj-4987 题目大意:给定一颗n个点的有边权的树,选出k个点,使得:$\sum\limits_{i=1}^{k-1}dis_idis_j$最小. 注释:$1\le n\le 3000 ...

  9. Ubuntu 16.04中CPU轮流100%的问题解决

    刚装好Ubuntu 16.04,但是观察各个CPU都是轮流100%,如图所示:

  10. 喜欢玩warcraft的ltl

    喜欢玩warcraft的ltl 时间限制:2000 ms  |  内存限制:65535 KB 难度:3 描写叙述 ltl 很喜欢玩warcraft.由于warcraft十分讲究团队总体实力,而他自己如 ...