zoukankan      html  css  js  c++  java
  • Walk

    【问题描述】
    给定一棵n个节点的树,每条边的长度为 1,同时有一个权值
    w。定义一条路径的权值为路径上所有边的权值的最大公约数。现在
    对于任意i∈[1,n],求树上所有长度为 i的简单路径中权值最大的
    是多少。如果不存在长度为 i的路径,则第 i行输出 0。
    【输入格式】
    第一行,一个整数 n,表示树的大小。
    接下来n-1行,每行三个整数u,v,w,表示u,v间存在一条权值
    为w的边。
    【输出格式】
    对于每种长度,输出一行,表示答案。
    【样例输入】
    3
    1 2 3
    1 3 9
    【样例输出】
    9
    3
    0
    【数据范围及约定】
    对于30%的数据,n≤1000。
    对于额外30%的数据,w≤100。
    对于100%的数据,n≤4*10^5,1≤u,v≤n,w≤10^6。

    分析:
    树上的操作,
    嗯很好,我们要在边上做文章

    考虑一下gcd的性质,
    我们不需要什么厉害的结论
    gcd(a,b)<=min(a,b) 显然吧

    考虑边的取值不大,可以枚举一下gcd
    假设枚举的gcd是i
    那么什么样的路径可以使答案=i哩
    当然是权值>i的边啦
    废话。。。光是权值大没有用啊,ta还要有i这个因子啊
    换句话说就是权值要是i的倍数
    这样我们就可以在枚举gcd的时候,
    加入相应的边,找到一条树上的最长路径,更新答案,

    嗯,好像已经很完美了
    等等,我们怎么在这么多边中找到一个特定权值的所有变呢
    难道要O(n)遍历吗
    显然不行,
    这里就有一个小技巧了:
    平常我们在加边的时候,大概都是这么写:

    void add(int u,int w,int z)
    {
        tot++;
        way[tot].x=u;
        way[tot].y=w;
        way[tot].v=z;
        way[tot].nxt=st[u];
        st[u]=tot;
    }

    st数组是以每一个起点为端点的所有边的起点值
    那么我们能不能以权值为关键字呢
    当然可以:有想法就有未来!!!
    那么存边的时候就变成了这样:

    void Add(int u,int w,int z)
    {  //用权值当做关键字 
        tt++;
        way[tt].x=u;
        way[tt].y=w;
        way[tt].nxt=sta[z];
        sta[z]=tt;
    }
    这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace std;
    
    const int N=400005;
    struct node{
        int x,y,nxt;
    };
    node e[N],way[N<<1];
    int st[N],sta[1000010],tt=0,tot=0;
    int p[N<<1],num=0;  //判重
    int ans[N],mxlen,mx,n,link[N<<1]; 
    
    void Add(int u,int w,int z)  //用权值当做关键字
    {
        tt++;
        e[tt].x=u;e[tt].y=w;e[tt].nxt=sta[z];sta[z]=tt;
        //单项加边即可 
    }
    
    void add(int u,int w)
    {
        tot++;
        way[tot].x=u;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;link[tot]=u;
        tot++;
        way[tot].x=w;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;link[tot]=w;
        //link的意思好像就是每条边的起点,不知道ta存在的意义是什么 
    }
    
    int dfs(int now)  //找树上最长路径 
    {
        p[now]=num;  //visit;
        int mxson=0;
        for (int i=st[now];i;i=way[i].nxt)
            if (p[way[i].y]!=num)  //not visit
            {
                int r=dfs(way[i].y);
                mxlen=max(mxlen,r+mxson+1);
                mxson=max(mxson,r+1);
            }
        return mxson;
    }
    
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<n;i++)
        {
            int u,w,z;
            scanf("%d%d%d",&u,&w,&z);
            mx=max(mx,z); //找到最大权值  
            Add(u,w,z);
        }
        for (int i=1;i<=mx;i++) //mx是gcd上界 
        {   //i是我们枚举的gcd 
            for (int j=i;j<=mx;j+=i)
                for (int k=sta[j];k;k=e[k].nxt)   //相同权值的加入树中
                    add(e[k].x,e[k].y);
            mxlen=0;
            num++;
            for (int k=1;k<=tot;k++)
                if (p[link[k]]!=num) dfs(link[k]);
            for (int k=1;k<=tot;k++) st[link[k]]=0;
            //memset(st,0,sizeof(st));
            tot=0;
            ans[mxlen]=i;  //枚举的gcd 
        }
        for (int i=n-1;i>0;i--) ans[i]=max(ans[i],ans[i+1]);  //ans一定是不增的 
        for (int i=1;i<=n;i++) printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    DFS 算法总结
    拆分Cocos2dx渲染部分代码
    [OpenGL]纹理贴图实现 总结
    [LeetCode] Minimum Size Subarray Sum 最短子数组之和
    LRU Cache 题解
    Substring with Concatenation of All Words 题解
    multimap和multiset 认知和使用
    OpenGL ES 3.0 基础知识
    Cocos2dx坐标转换
    视图
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673466.html
Copyright © 2011-2022 走看看