树链剖分-点的分治(点数为k且距离最长的点对)
Shortest-path tree
We may construct a shortest-path tree using the following method:
We consider a shortest-path tree rooted at node 1. For every node i in the graph G, we choose a shortest path from root to i. If there are many shortest paths from root to i, we choose the one that the sequence of passing nodes' number is lexicographically
minimum. All edges on the paths that we chose form a shortest-path tree.
Now we want to know how long are the longest simple paths which contain K nodes in the shortest-path tree and how many these paths? Two simple paths are different if the sets of nodes they go through are different.
For each test case, the first line contains three integers n, m, k(1<=n<=30000,1<=m<=60000,2<=k<=n), denote the number of nodes, the number of edges and the nodes of required paths.
Then next m lines, each lines contains three integers a, b, c(1<=a, b<=n, 1<=c<=10000),denote there is an edge between a, b and length is c.
1
6 6 4
1 2 1
2 3 1
3 4 1
2 5 1
3 6 1
5 6 1
3 4
题意:
题意其实就是给你一个图,然后让你转换成一棵树,这棵树满足的是根节点1到其余各点的间隔都是图里的最短间隔,并且为了包管这棵树的独一性,路径也必须是最小的。转化成树的办法其实就是跑一次spfa。spfa的时辰记下所有到这个的前驱的边,然后这些边集反向的边补上就是构成所有最短路的边。然后在这些边上跑一次dfs,跑前将边遵守达到点的序号由小到大排序,重视dfs搜的下一个点的间隔必须是最短的才搜,不然的话搜出来的图就是不合错误的。
至此图的项目组转化完了,剩下的就是求一个图里包含了k个点的路径的最长间隔,以及有几许条,类似的题目还有有几许条路径的乘积=k,有几许条路径的和>k,有几许条路径的乘积是完全立方数。。。做法就是典范的树分治。
具体的做法是找出重心,对重心外的项目组递归求解,归并的时辰列举到重心的所有路径,列举的时辰可以用一个全局的map[s].k和map[s].dis分别记录不包含根节点的顶点个数是s的链的个数和该链的最长距离,时刻更新,然后用一个去列举新的项目组的路径,然后经由过程与mp存的信息对比更新答案;
网络赛的解题报告:
首先构造最短路径树。先求根节点到其他节点的最短路径,然后从根节点开始进行深度优先遍历,先遍历节点编号较小的没遍历过的儿子,这样就能处理处最短路径树。
之后找节点数为K的树链。可以用树分治进行求解,在树分治求解过程中,对于每个中心点,处理出该子树中所有节点到中心点的树链,然后枚举每条树链,比如某条树链节点为a,长度为b,则找之前遍历过的树链中节点数位K-a的长度最长的树链,将这两条树链拼起来可以得到节点数为K的树链,用其更新答案。最好一个一个分支分别处理,可以避免考虑同一个分支来的两条树链。这样枚举并且更新信息,还要存方案数。
程序:
#include"stdio.h"
#include"string.h"
#include"iostream"
#include"map"
#include"string"
#include"queue"
#include"stdlib.h"
#include"math.h"
#define M 30009
#define eps 1e-10
#define inf 1000000000
#define mod 1000000000
using namespace std;
struct node
{
int u,w,v,next;
}edge[M*4];
struct Edge
{
int u,v,w;
}p[M*4];
struct st
{
int dis;
__int64 k;
}mp[M];
int t,k,cnt,maxi,MN,ID;
__int64 ans;
int head[M],use[M],dis[M],id[M],num[M],son[M],limit[M];
int cmp(const void *a,const void *b)
{
if((*(struct node*)a).u==(*(struct node*)b).u)
return (*(struct node*)b).v-(*(struct node*)a).v;
else
return (*(struct node*)b).u-(*(struct node*)a).u;
}
void init()
{
t=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v,int w)
{
edge[t].u=u;
edge[t].v=v;
edge[t].w=w;
edge[t].next=head[u];
head[u]=t++;
}
void spfa(int s,int n)
{
queue<int>q;
int i;
for(i=1;i<=n;i++)
dis[i]=inf;
memset(use,0,sizeof(use));
dis[s]=0;
use[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
use[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(dis[v]>dis[u]+edge[i].w)
{
dis[v]=dis[u]+edge[i].w;
p[v].u=u;
p[v].w=edge[i].w;
if(!use[v])
{
use[v]=1;
q.push(v);
}
}
}
}
}
void dfs_size(int u,int f)
{
son[u]=1;
limit[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(f!=v&&!use[v])
{
dfs_size(v,u);
son[u]+=son[v];
limit[u]=max(limit[u],son[v]);
}
}
}
void dfs_root(int root,int u,int f)
{
if(son[root]-son[u]>limit[u])
limit[u]=son[root]-son[u];
if(MN>limit[u])
{
MN=limit[u];
ID=u;
}
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(f!=v&&!use[v])
{
dfs_root(root,v,u);
}
}
}
void get_root(int root,int u,int f)
{
dfs_size(u,f);
MN=inf;
dfs_root(root,u,f);
}
void dfs_dis(int u,int f,int Rank,int w)
{
id[cnt++]=u;
num[u]=Rank;
dis[u]=w;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(f!=v&&!use[v])
{
dfs_dis(v,u,Rank+1,w+edge[i].w);
}
}
}
void cal(int u,int f)
{
cnt=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(f==v||use[v])continue;
int last=cnt;
dfs_dis(v,u,2,edge[i].w);
for(int j=last;j<cnt;j++)
{
if(num[id[j]]==k)//根节点的子链点的个数刚好是k
{
if(maxi<dis[id[j]])//更新最长距离,同时更新数量
{
maxi=dis[id[j]];
ans=1;
}
else if(maxi==dis[id[j]])//相等的话ans++;
{
ans++;
}
}
else if(num[id[j]]<k&&mp[k-num[id[j]]].k)//当当前子链点数小于k,则寻找k-num的子链,
{
if(maxi<dis[id[j]]+mp[k-num[id[j]]].dis)//更新最长距离
{
maxi=dis[id[j]]+mp[k-num[id[j]]].dis;
ans=mp[k-num[id[j]]].k;
}
else if(maxi==dis[id[j]]+mp[k-num[id[j]]].dis)//有相等的话ans加上k-num的链的个数
{
ans+=mp[k-num[id[j]]].k;
}
}
}
for(int j=last;j<cnt;j++)//转化为对立链(即不包含根节点的链)
{
if(mp[num[id[j]]-1].dis<dis[id[j]])
{
mp[num[id[j]]-1].dis=dis[id[j]];
mp[num[id[j]]-1].k=1;
}
else if(mp[num[id[j]]-1].dis==dis[id[j]])
{
mp[num[id[j]]-1].k++;
}
}
}
for(int i=0;i<cnt;i++)//注意此时更新mp不要写成memset的形式,否则会超时
mp[i].k=mp[i].dis=0;
}
void dfs_ans(int u,int f)
{
get_root(u,u,f);
cal(ID,ID);
use[ID]=1;
for(int i=head[ID];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(!use[v])
{
dfs_ans(v,v);
}
}
}
void solve()
{
memset(mp,0,sizeof(mp));
memset(use,0,sizeof(use));
ans=maxi=0;
dfs_ans(1,1);
printf("%d %I64d\n",maxi,ans);
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n,m,i;
scanf("%d%d%d",&n,&m,&k);
for(i=0;i<m*2;i+=2)
{
scanf("%d%d%d",&p[i].u,&p[i].v,&p[i].w);
p[i^1].u=p[i].v;
p[i^1].v=p[i].u;
p[i^1].w=p[i].w;
}
init();
qsort(p,m*2,sizeof(p[0]),cmp);
for(i=0;i<m*2;i++)
add(p[i].u,p[i].v,p[i].w);
spfa(1,n);
init();
for(i=2;i<=n;i++)
{
add(i,p[i].u,p[i].w);
add(p[i].u,i,p[i].w);
}
solve();
}
}
树链剖分-点的分治(点数为k且距离最长的点对)的更多相关文章
- 树链剖分 (求LCA,第K祖先,轻重链剖分、长链剖分)
2020/4/30 15:55 树链剖分是一种十分实用的树的方法,用来处理LCA等祖先问题,以及对一棵树上的节点进行批量修改.权值和查询等有奇效. So, what is 树链剖分? 可以简单 ...
- 树链剖分-点的分治(链的点的个数为k的点对数)
hdu4760 Cube number on a tree Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65535/65535 ...
- 树链剖分-点的分治(dis[i]+dis[j]==k的点对数量)
poj2114 Boatherds Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 1195 Accepted: 387 ...
- [2016北京集训试题7]thr-[树形dp+树链剖分+启发式合并]
Description Solution 神仙操作orz. 首先看数据范围,显然不可能是O(n2)的.(即绝对不是枚举那么简单的),我们考虑dp. 定义f(x,k)为以x为根的子树中与x距离为k的节点 ...
- BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector
题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现她们面临着一个 ...
- 树分治&树链剖分相关题目讨论
预备知识 树分治,树链剖分 poj1741 •一棵有n个节点的树,节点之间的边有长度.方方方想知道,有多少个点对距离不超过m 题解 点分治模板题.详见我早上写的http://www.cnblogs ...
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
- dsu+树链剖分+树分治
dsu,对于无修改子树信息查询,并且操作支持undo的问题 暴力dfs,对于每个节点,对所有轻儿子dfs下去,然后再消除轻儿子的影响 dfs重儿子,然后dfs暴力恢复轻儿子们的影响,再把当前节点影响算 ...
- [luogu4886] 快递员(点分治,树链剖分,lca)
dwq推的火题啊. 这题应该不算是点分治,但是用的点分治的思想. 每次找重心,算出每一对询问的答案找到答案最大值,考虑移动答案点,使得最大值减小. 由于这些点一定不能在u的两颗不同的子树里,否则你怎么 ...
随机推荐
- AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题
AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ...
- 如何研究某个gene的ceRNA 网络
研究人员针对 PTEN 这个关键的抑癌基因,来探究调控该基因表达的ceRNA 网络: 分析策略: 1)预测能调控该基因的miRNAs 通过miRanda 软件预测和实验验证相结合的方式,挑选出 miR ...
- Discuz论坛post登录C#源码
总结: loginhash formhash 表单参数 seccode 参数最重要 全局 的 获取验证码 判断验证码 到最后提交登录 它都有存在 ,seccode==idhash COOKIE自动维 ...
- android基站定位程序获取地理位置
目录 一.设置界面 二.为按钮绑定事件 三.获取基站信息 四.获取经纬度 五.获取物理位置 六.显示结果 七.运行程序 八.总结 九.程序代码 正文 在Android操作系统下,基站定位其实很简单,先 ...
- MathType中输入破折号的教程
MathType公式编辑器中的包含的各种数学符号与模板已经足够我们在编辑公式时使用了,但是除此之外,MathType还有一些符号并不是数学专有的符号,但是在数学中也偶尔会用到,比如破折号.MathTy ...
- 利用MSSQL对不经常使用的表进行依赖缓存
缓存是我们开发应用系统的一把利刃,如果用的不好,会导致数据不准确等一系列问题. 所以在如何选择缓存的时候,我们要慎之又慎.所以在对系统中一些 不经常变化的表,我们可以采用SqlCacheDenpend ...
- mysql中,查看当前数据库下所有的基表,不包括视图
环境描述: mysql版本:5.5.57-log 操作系统版本:Red Hat Enterprise Linux Server release 6.6 (Santiago) 需求描述: 查看当前使用的 ...
- QQ第三方登录实例demo(QQSDK包优化)
实现效果: 实现流程: 1.注冊QQ互联开发人员 QQ互联官网 注冊成为开发人员(须要审核) 2.审核通过之后 申请应用(须要互联人员审核*须要备案成功的线上域名) 以下我们開始下载QQsdk包 QQ ...
- Extjs学习笔记--(四,基本函数介绍)
Ext是Extjs的命名空间,为Extjs框架提供唯一的全局变量 这样做可以避免冲突,便于代码维护 1,apply和applyif方法 apply=function(object, config, d ...
- Swift-基础语法之变量&常量&元组
使用 let来声明一个常量,用 var来声明一个变量.常量的值在编译时并不要求已知,但是你必须为其赋值一次.这意味着你可以使用常量来给一个值命名,然后一次定义多次使用 myVariable = let ...