zoukankan      html  css  js  c++  java
  • E 旅游方案(travel)(南阳理工学院2019年校赛)

    E 旅游方案(travel)

    小 C 是一位著名的旅游家。他准备规划他在 K 国的旅游路线。

    K 国有若干座城市,每个城市有一个唯一的编号。城市和城市之间有一些道路相连,每条 道路只在某一天开放。小 C 的旅游路线有包括三个参数:u, v, t,表示小 C 准备在第 t 天旅 游,路线的起点为城市 u,终点为城市 v。此外,旅游路线还必须满足以下要求:

    • u < v,即出发城市的编号必须比终点城市的编号小;

    • 在第 t 天中,小 C 能够沿着开放的道路从 u 走到 v。

    请帮小 C 计算出不同的旅游方案数,即满足上述条件的三元组 (u, v, t) 的个数。小 C 只关 注路线的起点和终点,也就是说,即使在某一天小 C 能够以多种方式从起点城市 u 到终点城 市 v,在计算方案数时只计算一次。但是,如果能够在不同的几天从起点城市 u 到终点城市 v,在计算方案数时需要计算多次。

    例如,假设 K 国有 3 座城市和 3 条道路,编号分别为 1,2,3,第一条道路连接城市 1 和 城市 2,第二条道路连接城市 2 和城市 3,第三条道路连接城市 1 和城市 3。其中,第 1 和第 2 条道路只在第 1 天开放,第 3 条道路只在第 2 天开放,如下图所示。

    img

    合法的旅游方案共有 4 种:

    1. 在第 1 天,以城市 1 为起点,城市 2 为终点;

    2. 在第 1 天,以城市 1 为起点,城市 3 为终点;

    3. 在第 1 天,以城市 2 为起点,城市 3 为终点;

    4. 在第 2 天,以城市 1 为起点,城市 3 为终点。

    输入描述:

    输入一个整数 n, m (2 ≤ n ≤ 100000, 1 ≤ m ≤ 100000),表示城市的数量和道路的数量。城市的编号从 1 到 n。
    
    接下来的 m 行,每行包括三个整数 u, v, t (1 ≤ u < v ≤ n, 1 ≤ t ≤ 100000),表示在城市 u和城市 v 之间有一条双向道路,这条道路只在第 t 天开放。在同两座城市之间可能存在多条道路。
    

    输出描述:

    在一行内输出一个整数,表示不同的旅游方案数。
    

    样例输入:

    3 3
    1 2 1
    2 3 1
    1 3 2
    

    样例输出:

    4
    

    提示:

    img

    分析:

    ​ 对于每一天可以通行的边,建立一个图,求出该图每一个联通块的顶点个数。对于每nn个节点的联通块, 从​uu到 ​vv 的路径有​n(n1)/2n*(n-1)/2种 ,用ans将每天的每个联通块的可行路径数加起来即可。具体实现可分下列步骤

    1. 对所有的边,根据天数从小到大排序 (方便找到某一天的所有边的区间)
    2. 对于每一天的的所有边,用并查集的方法在祖先节点记录该集合的顶点数
    3. 在第二部的基础上,遍历所有这一天所有出现边的顶点,如果该顶点是祖先就统计该集合的顶点数nn,从而把n(n1)/2n*(n-1)/2​ 加到ans上。

    ##代码:

    #include<bits/stdc++.h>
    #define mset(a,b)   memset(a,b,sizeof(a))
    using namespace std;
    const int maxn=1e5+20;
    struct Node{
    int u,v,t;
    }edge[maxn];
    bool cmp(Node a,Node b)
    {
        return a.t<b.t;
    }
    int book[maxn];
    int father[maxn],tot[maxn];//并查集  tot代表个数
    int Find(int x)
    {
        if(x==father[x])
            return x;
        return father[x]=Find(father[x]);
    }
    ll calc(ll a)
    {
        return a*(a-1)/2;
    }
    ll solve(int l,int r)//对该区间内进行求解
    {
        for(int i=l;i<=r;++i)//初始化
        {
            father[edge[i].u]=edge[i].u;
            father[edge[i].v]=edge[i].v;
            tot[edge[i].u]=1;
            tot[edge[i].v]=1;
        }
        /*并查集*/
        for(int i=l;i<=r;++i)
        {
            int root1,root2;
            root1=Find(edge[i].u);
            root2=Find(edge[i].v);
            if(root1!=root2)
            {
                tot[root1]+=tot[root2];
                father[root2]=root1;
            }
        }
        ll ans=0;
        for(int i=l;i<=r;++i)
        {
            int u=edge[i].u;
            if(father[u]==u&&tot[u]!=0)
            {
                ans+=calc(tot[u]);
                tot[u]=0;
            }
            u=edge[i].v;
            if(father[u]==u&&tot[u]!=0)
            {
                ans+=calc(tot[u]);
                tot[u]=0;
            }
        }
        return ans;
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;++i)
        {
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].t);
        }
        sort(edge,edge+m,cmp);
        int l=0;
        ll ans=0;
        for(int i=0;i<m;++i)
        {
            if(i==m-1||edge[i].t!=edge[i+1].t)
            {
                ans+=solve(l,i);
                l=i+1;
            }
        }
        cout<<ans<<endl;
        return 0;
    }
    
  • 相关阅读:
    进度条简单实现
    bootstrap学习(二)-----Modal模态框
    PL/SQL Developer登入时候报ORA-12638: 身份证明检索失败的解决办法
    pdf.js在IIS中配置使用笔记
    JSON数据查询方法
    Visual Studio 2013 错误提示“未找到与约束匹配”的修正
    WebStorm 11激活方法
    Xamarin开发Android笔记:使用ZXing进行连续扫描
    Xamarin开发IOS笔记:切换输入法时输入框被遮住
    Xamarin开发Android笔记:拍照或相册选取图片角度问题
  • 原文地址:https://www.cnblogs.com/dchnzlh/p/10546539.html
Copyright © 2011-2022 走看看