[FJOI2014]最短路径树问题

LG传送门

B站传送门

长链剖分练手好题。

如果你还不会长链剖分的基本操作,可以看看我的总结

这题本来出的很没水平,就是dijkstra(反正我是不用SPFA)的板子强行套个点分治的板子,两者之间没有任何关联。但我偏要写长链剖分。

首先给你的是一张图,要转成一棵题目所要求的树,先把出边按到达点的编号排个序再跑个单源最短路,然后一遍dfs把树建出来就好了。这不是本文的重点(如果用点分治也要这样写),如果没搞懂看看代码就好了。

建出一棵树之后,把某个点连向其父亲的边的边权定为这个点的点权(我写长链剖分习惯处理点权),然后就可以写DP了,按照一般套路先写\(n^2\)DP。容易想到设\(f[x][j]\)表示\(x\)子树中从\(x\)往下延伸的深度为\(j\)的链上的最大点权和,设\(c[x][j]\)表示满足上述条件的链的条数,合并轻儿子时用轻儿子和重儿子的信息更新答案,再用轻儿子的信息更新重儿子的信息,最后用重链上的信息更新一下答案。

但是我们发现在一个点\(i\)继承其重儿子\(q[x]\)的信息时,需要对\(f[x][]\)中的每一个数加上\(q[x]\)的点权,而直接这样做是\(O(n)\)的,那我们长链剖分的复杂度就会掉到\(O(n^2)\),和暴力没区别。于是考虑优化。

开一个标记数组\(w[x]\)(可以直接用记录点权的数组,我就是这样写的),表示在处理\(f[x][]\)数组时,里面每一个数的实际值都是记录在数组里的值加上\(w[i]\)的结果。转移标记就是\(w[x]+=w[q[x]]\)。在合并轻儿子时,用\(f[x][K-j]+f[y][j]+w[q[x]]+w[y]\)来更新答案(\(q[x]\)是重儿子,\(K\)的值是题目所给的值\(-2\)(至于为什么,可以自己手玩),\(y\)是轻儿子),这样就统计了跨过点\(x\)的答案,然后用\(f[y][j]+w[y]-w[q[x]]\)来更新\(f[x][j+1]\)的信息,再用\(f[x][K+1]+w[q[x]]\)来统计一下重链上的答案就好了。但是要注意每个\(f[x][0]=-w[q[x]]\),这样是为了方便转移。从我的描述中就可以看到,我们打的标记并不会对复杂度产生影响,仍是优秀的\(O(n)\)。

实现起来就是细节有点多,打的时候注意。(\(f[x][j]\)和\(c[x][j]\)是用结构体记的)

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define R register
#define I inline
#define P push_back
#define M make_pair
#define O first
#define T second
using namespace std;
const int S=30003;
char buf[S],*p1,*p2;
I char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}
I int rd(){
R int f=0; R char c=gc();
while(c<48||c>57) c=gc();
while(c>47&&c<58) f=f*10+(c^48),c=gc();
return f;
}
struct N{int f,c;N(){f=0,c=1;}}u[S],*f[S],*e=u+1;
int h[S],s[S],g[S],b[S],k[S],d[S],t[S],p[S],q[S],w[S],c,o,l,K;
vector<pair<int,int> > v[S]; priority_queue<pair<int,int> > Q;
I int max(int x,int y){return x>y?x:y;}
I void add(int x,int y){s[++c]=h[x],h[x]=c,g[c]=y,b[y]=1,p[y]=x;}
void dfs0(int x){
for(R int i=0,y,z,m=v[x].size();i<m;++i)
if(!b[y=v[x][i].O]&&k[x]+(z=v[x][i].T)==k[y]) add(x,y),w[y]=z,dfs0(y);
}
void dfs1(int x){t[x]=d[x]=d[p[x]]+1;
for(R int i=h[x],y;i;i=s[i])
if((y=g[i])^p[x]){dfs1(y);
if(t[y]>t[x]) t[x]=t[y],q[x]=y;
}
}
void dfs2(int x){f[x][0].c=1;
R int i,j,y,z,u,v,m; if(!q[x]) return ;
f[q[x]]=f[x]+1,dfs2(q[x]),w[x]+=(v=w[q[x]]),f[x][0].f-=v;
for(i=h[x];i;i=s[i])
if((y=g[i])^p[x]&&y^q[x]){f[y]=e,m=t[y]-d[y]+1,e+=m,dfs2(y),u=w[y];
for(j=max(K-t[x]+d[x],0);j<m&&K-j;++j)
if((z=f[y][j].f+f[x][K-j].f+u+v)>o) o=z,l=f[y][j].c*f[x][K-j].c;
else if(o==z) l+=f[x][K-j].c*f[y][j].c;
for(j=0;j<m;++j)
if((z=f[y][j].f+u-v)>f[x][j+1].f)
f[x][j+1].f=z,f[x][j+1].c=f[y][j].c;
else if(z==f[x][j+1].f) f[x][j+1].c+=f[y][j].c;
}
if(K+1>t[x]-d[x]+1) return ;
if((z=f[x][K+1].f+v)>o) o=z,l=f[x][K+1].c; else if(o==z) l+=f[x][K+1].c;
}
int main(){
R int n=rd(),m=rd(),i,x,y,z;
for(K=rd()-2,i=1;i<=m;++i) x=rd(),y=rd(),z=rd(),v[x].P(M(y,z)),v[y].P(M(x,z));
for(i=1;i<=n;++i) sort(v[i].begin(),v[i].end());
memset(k,0x3f,sizeof k),k[1]=0,Q.push(M(0,1));
while(!Q.empty()){x=Q.top().T,Q.pop();
if(b[x]) continue; b[x]=1;
for(i=0,m=v[x].size();i<m;++i)
if(k[y=v[x][i].O]>k[x]+(z=v[x][i].T))
k[y]=k[x]+z,Q.push(M(-k[y],y));
}
memset(b,0,sizeof b),dfs0(1),dfs1(1),f[1]=e,e+=t[1],dfs2(1);
printf("%d %d",o,l);
return 0;
}

本来以为可以复杂度踩点分治,仔细一看才想起dij的复杂度是\(O(nlogn)\)的,总的复杂度还是\(nlogn\),只能夸夸自己常数小了。

[FJOI2014]最短路径树问题 长链剖分的更多相关文章

  1. 2018.11.03 NOIP模拟 树(长链剖分优化dp)

    传送门 考虑直接推式子不用优化怎么做. 显然每一个二进制位分开计算贡献就行. 即记录fi,jf_{i,j}fi,j​表示距离iii这个点不超过jjj的点的每个二进制位的0/10/10/1个数. 但直接 ...

  2. CF1009F Dominant Indices(树上DSU/长链剖分)

    题目大意: 就是给你一棵以1为根的树,询问每一个节点的子树内节点数最多的深度(相对于这个子树根而言)若有多解,输出最小的. 解题思路: 这道题用树链剖分,两种思路: 1.树上DSU 首先想一下最暴力的 ...

  3. Bzoj4016/洛谷P2993 [FJOI2014] 最短路径树问题(最短路径问题+长链剖分/点分治)

    题面 Bzoj 洛谷 题解 首先把最短路径树建出来(用\(Dijkstra\),没试过\(SPFA\)\(\leftarrow\)它死了),然后问题就变成了一个关于深度的问题,可以用长链剖分做,所以我 ...

  4. Luogu2993 FJOI2014 最短路径树问题 最短路树、长链剖分

    传送门 强行二合一最为致命 第一问直接最短路+$DFS$解决 考虑第二问,与深度相关,可以考虑长链剖分. 设$f_{i,j}$表示长度为$i$,经过边数为$j$时的最大边权和,考虑到每一次从重儿子转移 ...

  5. 2018牛客网暑假ACM多校训练赛(第七场)I Tree Subset Diameter 动态规划 长链剖分 线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round7-I.html 题目传送门 -  https://www.n ...

  6. [LOJ3014][JOI 2019 Final]独特的城市——树的直径+长链剖分

    题目链接: [JOI 2019 Final]独特的城市 对于每个点,它的答案最大就是与它距离最远的点的距离. 而如果与它距离为$x$的点有大于等于两个,那么与它距离小于等于$x$的点都不会被计入答案. ...

  7. BZOJ.3653.谈笑风生(长链剖分/线段树合并/树状数组)

    BZOJ 洛谷 \(Description\) 给定一棵树,每次询问给定\(p,k\),求满足\(p,a\)都是\(b\)的祖先,且\(p,a\)距离不超过\(k\)的三元组\(p,a,b\)个数. ...

  8. BZOJ1758[Wc2010]重建计划——分数规划+长链剖分+线段树+二分答案+树形DP

    题目描述 输入 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai, ...

  9. BZOJ.3252.攻略(贪心 长链剖分/线段树)

    题目链接 贪心,每次选价值最大的一条到根的链.比较显然(不选白不选). 考虑如何维护这个过程.一个点的价值选了就没有了,而它只会影响它子树里的点,可以用DFS序+线段树修改.而求最大值也可以用线段树. ...

随机推荐

  1. Mac OS X文件系统的附加属性@如何彻底删除

    有时候在 Mac 系统下读写 NTFS 分区时,会发现一些文件不能打开. 显示错误为: 项目“XXX”已被 OS X 使用,不能打开. 如果再终端 ls -al 命令一下就可以看到: -rwxr-xr ...

  2. windows ionic bash: command not found

    安装好了node.js和npm后,执行npm install -g cordova ionic后,成功安装,但是执行ionic命令后,返回 command not found. 配置好了环境变量后,仍 ...

  3. stateless 无状态组件

    使用:

  4. webapi中的模型验证

    mic: https://docs.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/model-valida ...

  5. C# 4.0 不要跨程序集用dynamic指向匿名类型 (转载)

    今天写代码时偷懒用了dynamic,结果遇到问题,运行时始终无法获取dynamic对象的属性.原问题简化后如下: 程序集A包含SampleClass类,有一个静态方法,接收一个dynamic类型参数并 ...

  6. 在Eclipse中执行、配置Hadoop

    版权全部: zhe-jiang.he@hp.com  严禁转载! 1.安装插件 准备程序: eclipse-3.3.2(这个版本号的插件仅仅能用这个版本号的eclipse) hadoop-0.20.2 ...

  7. JAVA并发(一)

    java并发的一系列框架和技术主要是由java.util.concurrent 包所提供.包下的所有类可以分为如下几大类: locks部分:显式锁(互斥锁和速写锁)相关: atomic部分:原子变量类 ...

  8. #leetcode刷题之路31-下一个排列

    实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列.如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列).必须原地修改,只允许使用额外常数空间. 以下 ...

  9. 在js内生成PDF文件并下载的功能实现(不调用后端),以及生成pdf时换行的格式不被渲染,word-break:break-all

    在js内生成PDF文件并下载的功能实现(不调用后端),以及生成pdf时换行的格式不被渲染,word-break:break-all 前天来了个新需求, 有一个授权书的文件要点击下载, 需要在前端生成, ...

  10. 树莓派3B+学习笔记:11、查看硬件信息

    1.查看CPU信息 cat /proc/cpuinfo 查看最后三行 如果只想查看最后三行,也可使用这个命令 /proc/cpuinfo lscpu 2.查看树莓派型号 cat /proc/devic ...