我的树形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. 窥探原理:实现一个简单的前端代码打包器 Roid

    roid roid 是一个极其简单的打包软件,使用 node.js 开发而成,看完本文,你可以实现一个非常简单的,但是又有实际用途的前端代码打包工具. 如果不想看教程,直接看代码的(全部注释):点击地 ...

  2. Mac安装Qt出现错误Could not resolve SDK Path for 'macosx'

    Qt 5.8 + Mac 10.14  qdevice.pri文件里没有网上说的那行应该改的代码,自己写上这句话也没有解决问题 最终解决方案: 在命令行输入:sudo xcode-select -s ...

  3. Django-REST-Framework JWT 实现SSO认证(下)

    在上一篇博客中,我已经对JSON Web 认证做了简单的解释,这篇博客是续篇,若不了解,请看上一篇博客:https://www.cnblogs.com/yushenglin/p/10863184.ht ...

  4. react入门----组件的基础用法

    1.组件 <!-- React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件.React.createClass 方法就用于生成一个组件类 ...

  5. java List 数组删除元素

    在 java 中,ArrayList 是一个很常用的类,在编程中经常要对 ArrayList 进行增.删.改.查操作.之前在学校时一直认为删除操作是最简单的,现在才越发觉得自己愚蠢.只需要设置好预期条 ...

  6. java多线程编程核心技术(四)--Lock的使用

    1.jdk1.5中新增了ReentrantLock类,该类也可以实现synchronized线程之间同步互斥的效果. 2.jdk1.5中新增了Condition类.在Lock对象中可以创建多个Cond ...

  7. C. Star sky 二维前缀和

    time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standa ...

  8. 晶振虚焊导致TI 28335 DSP 烧写FLASH后,连接仿真器时正常工作,拔掉仿真器却不能启动运行

    遇到个诡异的问题,28335的DSP,之前程序调试一切正常,但是烧写FLASH后,拔掉仿真器却始终部工作. 解决思路: 1) 检查配置文件貌似没什么问题,复制到其他工程,在开发板上拔掉仿真器启动正常. ...

  9. java NPE就是空指针异常,null pointer exception

    java NPE就是空指针异常,null pointer exception

  10. Linux中W与Who命令的使用

    踢掉一个从某个终端连上的用户pkill -kill  -t pts/0 ---------------------------------------------------------------- ...