几道查询树上点之间的路径的题目

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<cstdio>
#include<cstring>
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<n;i++)
    {
        scanf("%d %d %d",&u,&v,&c);
        insert(u,v,c);
    }
    dfs(1);
    memset(visit,0,sizeof(visit));
    dfs2(1);
    printf("%lld\n",ans);
    return 0;
}
    


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<cstdio>
#include<cstring>
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))>>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))>>i;
        if(l[ro][!cnt]) ro=l[ro][!cnt],k+=(1<<i);
        else ro=l[ro][cnt];
    }
    return h[ro];
}
int main()
{
    freopen("xor.in","r",stdin);
    freopen("xor.out","w",stdout);
    int n;
    scanf("%d",&n);
    int u,v,c;
    for(int i=1;i<n;i++)
        scanf("%d %d %d",&u,&v,&c),insert(u,v,c);
    bfs();
    for(int i=1;i<=n;i++)    insert(dis[i]);
    long long int max=0,sum=0;
    for(int i=1;i<=n;i++)
    {
        k=0;int p=find(dis[i]);
        if(k>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<cstdio>
#include<cstring>
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<n;i++)
    {
        scanf("%d %d %d",&u,&v,&c);
        insert(u,v,c);
    }
    dfs1(1);
    memset(visit,0,sizeof(visit));
    dfs2(1);
    long long min=1e17;  

    for(int i=1;i<=n;i++)
    min=min<big[i]+f[i]?min:big[i]+f[i];
    printf("%lld\n",min);
    return 0;
}  

原文地址:https://www.cnblogs.com/Roni-i/p/9535595.html

时间: 2024-11-09 22:44:38

几道查询树上点之间的路径的题目的相关文章

计蒜客模拟赛D1T2 蒜头君的树:树上节点之间最短距离和

题目链接:https://nanti.jisuanke.com/t/16446 题意: 给你一棵有n个节点的树以及每条边的长度,输出树上节点之间的最短距离和.然后进行m次操作,每次操作更改一条边的长度,分别输出每次操作后树上节点之间的最短距离和. 题解: 最短距离和 = ∑(树上每一条边被最短路经过的次数 * 这条边的长度) 一个节点到它父节点的边被经过的次数 = 该节点以及它的子孙的节点个数 * 除了该节点和它子孙之外的所有节点总个数 每一个节点以及它子孙节点的个数总和用一遍dfs保存在num

在线查询树上最近公共祖先模板

在线查询树上最近公共祖先 标准题目 第一行有2个整数n和q:n代表树的结点数量,q代表询问的次数接下来n-1行每行两个整数u和v,表示结点u到结点v有一条边.然后给出q组询问,每组询问由两个整数a和b构成,询问节点a和b的最近公共祖先. 样例数据 input:8 31 31 23 43 53 62 72 84 56 75 8 output:311 代码 1. 邻接表建图 1 #include <iostream> 2 #include <cstdio> 3 #include <

tomcat/jetty容器之间的路径兼容性问题

在项目中使用springmvc框架时,在controller方法中返回的view路径字符串最后和xml文件配置中的配置路径进行整合,从而形成一个完成的视图文件路径,然后在tomcat和jetty身上两者之间的差异出现问题: <bean id="internalViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:viewClass=&q

「SQL归纳」树形结构表的存储与查询功能的实现——通过路径方法(非递归)

一.树形结构例子分析: 以360问答页面为例:http://wenda.so.com/c/ 我们通过观察URL,可以明确该页面的数据以树形结构存储,下面三块模块分别为: ①根节点 ②根节点的第一层子节点 ③为左侧所选择节点的下一层子节点 (图1) 该例简化的树形结构图如下: (图2) 我们不难发现,每当点击图1红框内的类别时,页面主体问题部分会显示该类别节点下所有子节点的问题.因此,需要实现查询出某节点所有子节点的功能. 二.表的存储: 需要存储两张表: 1.类别表 create table [

MySQL中明知道查询结果只有一个,SQL语句中使用LIMIT 1会提高查询效率

用户使用email作为用户名登陆的话,就需要查询出email对应的一条记录.每个用户的email是唯一的. SELECT * FROM t_user WHERE email=?; 上面的语句实现了查询email对应的一条用户信息,但是由于email这一列没有加索引,会导致全表扫描,效率会很低. SELECT * FROM t_user WHERE email=? LIMIT 1; 加上LIMIT 1,只要找到了对应的一条记录,就不会继续向下扫描了,效率会大大提高. 如果email是索引的话,就不

SQL Server 50道查询训练题,学生Student表

下面这个是题目所用到的数据库! 首先你需要在你的SQL Sever数据库中创建[TestDb]这个数据库,接下来下面这个代码.直接复制在数据库里运行就好了! 1 USE [TestDb] 2 GO 3 /****** Object: Table [dbo].[Course] Script Date: 2018/4/28 17:36:10 ******/ 4 SET ANSI_NULLS ON 5 GO 6 SET QUOTED_IDENTIFIER ON 7 GO 8 SET ANSI_PADD

一道int和unsigned char之间强制类型转换的题目

设有语句: ``` int a=258,b; unsigned char c; b=c=a; ``` 则b的值为___. (答案:2) (一)int 类型和char类型之间强制类型转换的规则 ```将int类型变量i的值赋给char类型变量c,会将i的值当作一个ascii码赋给c. int a=51; char c=a; cout<<c<<endl; ``` 输出结果是3 ```将char类型变量c的值赋给int类型变量i,会将c中存储的字符的ascii码当作一个整型数赋给i. c

浅析树链剖分

前言 树链剖分,我觉得最精妙的地方就在于它是通过$dfs$序将树形结构转为线性结构便于处理,进而可以用数据结构(线段树.树状数组等)去进行修改和查询. 将复杂的结构转化为相对我们熟悉简单的结构,这个思想对很多问题是通吃的,不仅仅在树形问题,算法中,在其他领域中也常常会用到这种思想 我们先来回顾两个问题: 1.将树从$x$到$y$结点最短路径上所有节点的值都加上z 我们很容易想到,树上差分可以以 $O(n+m)$的优秀复杂度解决这个问题 2.求树从$x$到$y$结点最短路径上所有节点的值之和 $l

BZOJ 2588 Count on a tree (COT) 可持久化线段树

题目大意:查询树上两点之间的第k大的点权. 思路:树套树,其实是正常的树套一个可持久化线段树.因为利用权值线段树可以求区间第k大,然后再应用可持久化线段树的思想,可以做到区间减法.详见代码. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define NIL (tree[0]) using name