• CJOJ 免费航班


    Description

    小Z在MOI比赛中获得了大奖,奖品是一张特殊的机 票。使用这张机票,可以在任意一个国家内的任意城市之间的免费飞行,只有跨国飞行时才会有额外的费用。小Z获得了一张地图,地图上有城市之间的飞机航班和 费用。已知从每个城市出发能到达所有城市,两个城市之间可能有不止一个航班。一个国家内的每两个城市之间一定有不止一条飞行路线,而两个国家的城市之间只 有一条飞行路线。小Z想知道,从每个城市出发到额外费用最大的城市,以便估算出出行的费用,请你帮助他。当然,你不能通过乘坐多次一个航班增加额外费用, 也就是必须沿费用最少的路线飞行。

    Input

    第一行,两个整数N,M,表示地图上有N个城市,M条航线。
    接下来M行,每行三个整数a,b,c,表示城市a,b之间有一条费用为c的航线。

    Output

    共N行,第i行为从城市i出发到达每个城市额外费用的最大值。

    Sample Input

    6 6
    1 4 2
    1 2 6
    2 5 3
    2 3 7
    6 3 4
    3 1 8

    Sample Output

    4
    4
    4
    6
    7
    7

    Hint

    样例说明
    有四个国家,包含的城市分别为 {1,2,3},{4},{5},{6}。从城市1出发到达城市6,乘坐(1,3)(3,6)两个航班费用最大,(1,3)在国内为免费航班,(3,6)的费用为4,所以从1出发的最大费用为4。

    数据规模
    对于30%的数据 1<=N<=1000,1<=M<=1000
    对于100%的数据 1<=N<=20000,1<=M<=200000

    Source

    动态规划 ,连通性

    根据题目描述,每个国家是一个边双连通分量,把每个边双连通分量缩点后,原图变为一棵树.

    相当于是求树上每个点在树上的最长路.

    所有点的树上最长路可以通过两边dfs进行DP;

    第一遍:求出每个点只到他子树内部的最长路和次长路

    第二遍:每个点再由他父亲来更新往改点的子树外走的最长路,因为该点到子树外面的路必经过他爸爸

    具体实现就是最长路和次长路转化,转移画画图就好了

    边双连通缩点的话就是把桥标记后在dfs一遍

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<vector>
    #define RG register
    using namespace std;
    typedef long long ll;
    const int N=20050;
    int gi(){
      int x=0;
      char ch=getchar();
      while(ch<'0'||ch>'9') ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
      return x;
    }
    int head[N],nxt[N*20],to[N*20],dfn[N],low[N],vis[N],zhan[N],w[N*20],cnt=1,tot,sum,top,pd[N*20];
    vector<int>q[N],p[N],W[N];
    int dis[N][2],vis2[N],fr[N];
    void tarjan(int x,int fa){
      dfn[x]=low[x]=++sum;
      for(RG int i=head[x];i;i=nxt[i]){
          int y=to[i];
          if(!dfn[y]){
    	  tarjan(y,x);
    	  low[x]=min(low[x],low[y]);
    	  if(low[y]>dfn[x]) pd[i]=pd[i^1]=1;
    	}
          else if(y!=fa) low[x]=min(low[x],dfn[y]);
        }
    }
    void dfs3(int x,int gg){
      fr[x]=gg,vis[x]=1;
      q[gg].push_back(x);
      for(RG int i=head[x];i;i=nxt[i]){
          if(!pd[i]&&!vis[to[i]]) dfs3(to[i],tot);
        }
    }
    void dfs1(int x){
        vis2[x]=1;
        for(RG int i=0;i<p[x].size();i++){
    	int y=p[x][i],w=W[x][i];
    	if(!vis2[y]){
    	    dfs1(y);
    	    if(dis[y][1]+w>=dis[x][1]){
    		dis[x][0]=dis[x][1];
    		dis[x][1]=dis[y][1]+w;
    	    }
    	    else dis[x][0]=max(dis[x][0],dis[y][1]+w);
    	}
        }
    }
    void dfs2(int x){
        vis2[x]=1;
        for(RG int i=0;i<p[x].size();i++){
    	int y=p[x][i],w=W[x][i];
    	if(!vis2[y]){
    	    if(dis[y][1]+w==dis[x][1]){
    		dis[y][0]=max(dis[y][0],min(dis[x][1]-w,dis[x][0]+w));
    		dis[y][1]=max(dis[x][1]-w,dis[x][0]+w);
    	    }
    	    else{
    		dis[y][0]=max(dis[y][0],max(dis[x][0]+w,min(dis[y][1],dis[x][1]+w)));
    		dis[y][1]=max(dis[y][1],dis[x][1]+w);
    	    }
    	    dfs2(y);
    	}
        }
    }
    int main(){
      int n,m,x,w1,y;
      cnt=1;n=gi(),m=gi();
      for(RG int i=1;i<=m;i++){
          x=gi(),y=gi(),w1=gi();
          to[++cnt]=y,w[cnt]=w1,nxt[cnt]=head[x],head[x]=cnt;
          to[++cnt]=x,w[cnt]=w1,nxt[cnt]=head[y],head[y]=cnt;
        }
      tarjan(1,1);
      for(int i=1;i<=n;i++) if(!vis[i]) dfs3(i,++tot);
      for(RG int i=1;i<=tot;i++)
        for(RG int j=0;j<q[i].size();j++)
          for(RG int k=head[q[i][j]];k;k=nxt[k]){
    	  int y=fr[to[k]],w1=w[k];
    	  if(y!=i){
    	      p[i].push_back(y);
    	      W[i].push_back(w1);
    	    }
    	}
      dfs1(1);for(RG int i=1;i<=tot;i++) vis2[i]=0;
      dfs2(1);for(RG int i=1;i<=n;i++) printf("%d
    ",dis[fr[i]][1]);
      return 0;
    }
    
  • 相关阅读:
    转载 NPOI.dll 用法。单元格,样式,字体,颜色,行高,宽度。读写excel
    Microsoft Visual SourceSafe 6.0 无法关联项目
    dynamic json
    c#查找string数组的某一个值的索引
    C#中 删除掉字符串数组中的空字符串
    c# 比较两个数组每一个值是否相等
    c#比较两个数组的差异
    C#动态操作DataTable(新增行、列、查询行、列等)
    C#数字转字母,ASCII码转换
    c#中使程序跳到指定行中
  • 原文地址:https://www.cnblogs.com/qt666/p/6880132.html
Copyright © 2020-2023  润新知