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


    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;  
    }  
    
  • 相关阅读:
    poj 1088 滑雪
    位运算与bitset
    hdu 4607 Park Visit
    树的直径
    codeforces 495D Sonya and Matrix
    German Collegiate Programming Contest 2015(第三场)
    BAPC 2014 Preliminary(第一场)
    Benelux Algorithm Programming Contest 2014 Final(第二场)
    E. Reachability from the Capital(tarjan+dfs)
    poj2104 K-th Number(划分树)
  • 原文地址:https://www.cnblogs.com/Roni-i/p/9535595.html
Copyright © 2020-2023  润新知