zoukankan      html  css  js  c++  java
  • 几道查询树上点之间的路径的题目

    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
    ",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
    ",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
    ",min);  
        return 0;  
    }  
    
  • 相关阅读:
    SQLite数据库框架ORMLite与GreenDao的简单比较
    Spring Web MVC中的页面缓存支持 ——跟我学SpringMVC系列
    admin嵌套在spring mvc项目里,菜单栏点击新连接每次都会重置
    Spring MVC视图层:thymeleaf vs. JSP
    使用Spring标签库
    SpringMVC中Controller跳转到另一个Controller方法
    有趣iOS开展
    Java日期的格式String类型GMT,GST换算成日期Date种类
    javascript之Style物
    Tair LDB基于Prefixkey中期范围内查找性能优化项目总结
  • 原文地址:https://www.cnblogs.com/Roni-i/p/9535595.html
Copyright © 2011-2022 走看看