博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
几道查询树上点之间的路径的题目
阅读量:5892 次
发布时间:2019-06-19

本文共 4721 字,大约阅读时间需要 15 分钟。

A 求和

时间限制: 1 Sec 空间限制: 256 MB

输入输出文件名:A.in,A.out

题目描述

给出一棵以1为根的有n个节点的树,树上每条边都有其边权。

求所有点对之间的路径上的边权和的总和。

输入格式:

第一行为n

接下来n-1行,每行三个整数,分别表示一条边的两端点编号和边权。(编号为1..n)

输出格式:

输出一个数字表示总和

输入样例

4

1 2 10

2 3 10

1 4 20

输出样例

130

样例解释

1->2:10 , 1->3:20 , 1->4:20 , 2->3:10 , 2->4:30 , 3->4:40 , 总和为130。

数据范围

对于30%的数据,1<=n<=300

对于80%的数据,1<=n<=3000

对于100%的数据,1<=n<=100000,边权为<=100的正整数。


这题的做法是考虑每条边对答案的贡献(套路题QAQ)

即考虑有多少条路径经过这条边

易知即边左边的点的数目*另一边的点的数目

我们可以先从根节点dfs一遍求出每个节点的子树的大小

即一边的点的数目

这样就可以得到贡献为 cost * size[e[k].to]*(n-size[e[k].to])

#include
#include
const int N=100005; struct node { int to,next,c; }e[N*2]; int first[N],visit[N],size[N]; int cnt=0; int n; void insert(int u,int v,int c) { e[++cnt].to=v;e[cnt].next=first[u];first[u]=cnt;e[cnt].c=c; e[++cnt].to=u;e[cnt].next=first[v];first[v]=cnt;e[cnt].c=c; } long long ans=0;long long sum=0; void dfs(int x) { visit[x]=1;size[x]=1; for(int k=first[x];k;k=e[k].next) if(!visit[e[k].to]) { dfs(e[k].to); size[x]+=size[e[k].to]; } } void dfs2(int x) { visit[x]=1; for(int k=first[x];k;k=e[k].next) { if(!visit[e[k].to]) { ans+=e[k].c*size[e[k].to]*(n-size[e[k].to]); dfs2(e[k].to); } } } int main() { scanf("%d",&n); int u,v,c; for(int i=1;i

Xor路

(xor.pas/c/cpp) 128MB 1s

给定一棵有N个点和N-1条边的树,请你求出树中的最长路径,以及总共有多少条最长路径。

这里路径长度是用xor定义的,即若经过的边的权值为a1, a2, a3,...,an,则这条路径的总权值为 a1 xor a2 xor a3 ... xor an。

输入格式

第1行为一个正整数 N,为点的个数。

第2行至第N行,每行包含三个正整数x,y,z,表示x和y之间有一条权值为z的边。

输出格式

仅一行,包含两个数字,为最长路径的长度和条数。

样例输入

4

1 2 3

2 4 1

1 3 4

样例输出

7 1

样例解释

2-1-3 这条路径,长度为3 xor 4=7。

数据范围

全部数据满足 0<=z<=1e9

(请注意栈空间溢出)

由xor的性质可得

两个点的路径即为根节点到一个点的路径^根节点到另一个点的路径

然后又由xor的性质可得

高位上两个值的二进制表示不同

异或的结果就为1(这样是最优的)

所以想到建一颗trie树

维护对一个值的最优异或结果

并统计方案数

#include
#include
const int N=200005; struct node { int next,to,c; }e[N*2]; int qu[N+100],dis[N];int cnt=0;int first[N],visit[N]; int l[N*31][2]; int h[N*31]; void insert(int u,int v,int c) { e[++cnt].to=v;e[cnt].next=first[u];e[cnt].c=c;first[u]=cnt; e[++cnt].to=u;e[cnt].next=first[v];e[cnt].c=c;first[v]=cnt; } void bfs() { int head=1,tail=2; qu[1]=1; while(head<=tail) { int rr=qu[head++];visit[rr]=1; for(int k=first[rr];k;k=e[k].next) if(!visit[e[k].to]) visit[e[k].to]=1,qu[tail++]=e[k].to,dis[e[k].to]=dis[rr]^e[k].c; } } int k=0; int tot=0; void insert(int x) { int ro=0; for(int i=30;i>=0;i--) { int cnt=(x&(1<
>i; if(!l[ro][cnt]) l[ro][cnt]=++tot; ro=l[ro][cnt]; } h[ro]++; } int find(int x) { int ro=0; for(int i=30;i>=0;i--) { int cnt=(x&(1<
>i; if(l[ro][!cnt]) ro=l[ro][!cnt],k+=(1<
max) max=k,sum=p; else if(k==max) sum+=p; } printf("%lld %lld\n",max,sum>>1); fclose(stdin); fclose(stdout); return 0; }

T3

时间限制: 1 Sec 空间限制: 256 MB

输入输出文件名:B.in,B.out

题目描述

给出一棵以1为根的有n个节点的树,树上每条边都有其边权。

四条琉璃想选择一个点作为起点,他希望这个起点到其他所有点距离和是最小的。

输入格式:

第一行为n

接下来n-1行,每行三个整数,分别表示一条边的两端点编号和边权。(编号为1..n)

输出格式:

一个数,表示那个最小的距离和。

输入样例

5

1 2 10

2 3 10

1 4 20

2 5 10

输出样例

60

样例解释

选2作为起点。2->1:10,2->3:10,2->4:30,2->5:10,总和为60

数据范围

对于40%的数据,1<=n,m<=2333

对于100%的数据,1<=n,m<=300 000,边权为<=100的正整数

这道题很多种方法可以做

方法一:

发现可以O(1)转移

先求出一个节点的答案

然后从一个点转移到另一个点

就+e[k].c * (n-size[e[k].to]) - e[k].c * size[e[k].to]

其实就是求的树的重心

方法二:s

双向树形dp

第一次dfs从儿子上传信息到父亲

即维护一个点到它的子树内每个点的距离之和(big数组)

第二次dfs从父亲下传信息到儿子

即维护到子树外的点的距离之和(f数组)(父亲和兄弟)

转移方程看代码吧

#include
#include
const int N=300050; struct node { int next,to,c; }e[N*2]; int first[N]; int cnt=0; long long int f[N]; int n; void insert(int u,int v,int c) { e[++cnt]=(node){first[u],v,c};first[u]=cnt; e[++cnt]=(node){first[v],u,c};first[v]=cnt; } long long int big[N]; int size[N],visit[N]; void dfs1(int x) { visit[x]=1;size[x]=1; for(int k=first[x];k;k=e[k].next) if(!visit[e[k].to]) { dfs1(e[k].to); size[x]+=size[e[k].to]; big[x]+=big[e[k].to]+(long long )e[k].c*size[e[k].to]; } } void dfs2(int x) { visit[x]=1; for(int k=first[x];k;k=e[k].next) if(!visit[e[k].to]) { f[e[k].to]=f[x]+(big[x]-big[e[k].to]-(long long )size[e[k].to]*e[k].c)+(n-size[e[k].to])*(long long )e[k].c; dfs2(e[k].to); } } int main() { freopen("B.in","r",stdin); freopen("B.out","w",stdout); scanf("%d",&n); int u,v,c; for(int i=1;i

转载于:https://www.cnblogs.com/Roni-i/p/9535595.html

你可能感兴趣的文章
设计模式:外观模式(Façade Pattern)
查看>>
ASP.NET中 DataList(数据列表)的使用前台绑定
查看>>
Linux学习之CentOS(八)--Linux系统的分区概念
查看>>
C语言字节对齐
查看>>
主域控制器的安装与配置步骤与方法
查看>>
调整Flash与div的位置关系
查看>>
Objective - c 创建二维数组
查看>>
〖Android〗/system/etc/fallback_fonts.xml
查看>>
30个美丽干净的,帮助用户专注于内容的网站设计
查看>>
高级Bash脚本编程指南(27):文本处理命令(三)
查看>>
JavaScript---事件
查看>>
Android NDK入门实例 计算斐波那契数列一生成jni头文件
查看>>
c/c++性能优化--I/O优化(上)
查看>>
将HTML特殊转义为实体字符的两种实现方式
查看>>
jquery 保留两个小数的方法
查看>>
网站架构设计的误区
查看>>
Standard C++ Programming: Virtual Functions and Inlining
查看>>
html5 Web Workers
查看>>
iis 故障导致网站无法访问
查看>>
作业抄袭简单检测
查看>>