[Usaco2011 Jan]道路和航线

Description
Farmer John正在一个新的销售区域对他的牛奶销售方案进行调查。他想把牛奶送到T个城镇 (1 <= T <= 25,000),编号为1T。这些城镇之间通过R条道路 (1 <= R <= 50,000,编号为1到R) 和P条航线 (1 <= P <= 50,000,编号为1到P) 连接。每条道路i或者航线i连接城镇A_i (1 <= A_i <= T)到B_i (1 <= B_i <= T),花费为C_i。对于道路,0 <= C_i <= 10,000;然而航线的花费很神奇,花费C_i可能是负数(-10,000 <= C_i <= 10,000)。道路是双向的,可以从A_i到B_i,也可以从B_i到A_i,花费都是C_i。然而航线与之不同,只可以从A_i到B_i。事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台 了一些政策保证:如果有一条航线可以从A_i到B_i,那么保证不可能通过一些道路和航线从B_i回到A_i。由于FJ的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。他想找到从发送中心城镇S(1 <= S <= T) 把奶牛送到每个城镇的最便宜的方案,或者知道这是不可能的。

Input

  • 第1行:四个空格隔开的整数: T, R, P, and S
  • 第2到R+1行:三个空格隔开的整数(表示一条道路):A_i, B_i 和 C_i
  • 第R+2到R+P+1行:三个空格隔开的整数(表示一条航线):A_i, B_i 和 C_i

Output

  • 第1到T行:从S到达城镇i的最小花费,如果不存在输出"NO PATH"。

Sample Input

6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10

样例输入解释:
一共六个城镇。在1-2,3-4,5-6之间有道路,花费分别是5,5,10。同时有三条航线:3->5,4->6和1->3,花费分别是-100,-100,-10。FJ的中心城镇在城镇4。

Sample Output

NO PATH
NO PATH
5
0
-95
-100

样例输出解释:
FJ的奶牛从4号城镇开始,可以通过道路到达3号城镇。然后他们会通过航线达到5和6号城镇。但是不可能到达1和2号城镇。

这题裸的单源最短路对吧,负边权直接上SPFA就好了。。。

然后你就可以获得TLE的好成绩,当然,不排除-Owys的优化

于是SPFA就有了一个优化,SLF优化,可以水过去,但是我没写

我们还是来讨论一下正解如何写。为什么这题不能用dijkstra,因为它有负边权。但是我们仔细观察发现,负边权只能是航线,而且航线只会连接两个无法直接到到的联通块,也就是说,我们可以在联通块内跑dijkstra

那么块与块之间的联系呢?这题缩完点后就是个DAG,那么我们就可以拓扑了,用拓扑处理块与块之间的关系,块内直接dijkstra,那么这题就做完了

然后有一些细节问题:

  • 拓扑序需要从S所在联通块开始,因此入度要做一些更改
  • 一个联通块开始dijkstra的时候,需要把所有的因为航线确定的点都扔到初始堆里面
/*program from Wolfycz*/
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 1e9
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)  print(x/10);
    putchar(x%10+'0');
}
const int N=2.5e4,M=5e4;
struct S1{
    int pre[(M<<1)+10],now[N+10],child[(M<<1)+10],val[(M<<1)+10],tot;
    void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;}
    void insert(int x,int y,int z){join(x,y,z),join(y,x,z);}
}Rod;
struct S2{
    int pre[M+10],now[N+10],child[M+10],val[M+10],tot;
    void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;}
}Pla;
struct S3{
    #define ls (p<<1)
    #define rs (p<<1|1)
    #define fa (p>>1)
    struct node{
        int x,v;
        bool operator <(const node &a)const{return v<a.v;}
    }Q[N+10];
    int tot;
    void insert(int x,int v){
        Q[++tot]=(node){x,v};
        int p=tot;
        while (p!=1&&Q[p]<Q[fa])    swap(Q[p],Q[fa]),p=fa;
    }
    void Delete(){
        Q[1]=Q[tot--];
        int p=1,son;
        while (ls<=tot){
            if (rs>tot||Q[ls]<Q[rs])    son=ls;
            else    son=rs;
            if (Q[son]<Q[p])    swap(Q[son],Q[p]),p=son;
            else    break;
        }
    }
}Heap;
int from[M+10],to[M+10];//航线边的起始和结束
int col[N+10],deg[N+10],dis[N+10];//点所属联通块编号;联通块度数;每个点的距离
int h[N+10];
bool vis[N+10];
vector<pair<int,int> >vec[N+10];
int n,m,q,S,size;
void dfs(int x){//大水漫灌法
    if (col[x]==size)   return;
    col[x]=size;
    for (int p=Rod.now[x],son=Rod.child[p];p;p=Rod.pre[p],son=Rod.child[p]) dfs(son);
}
void Get_deg(int x){//从S所在联通块开始,从新确定度数
    if (vis[x]) return;
    vis[x]=1;
    for (int p=Pla.now[x],son=Pla.child[p];p;p=Pla.pre[p],son=Pla.child[p]) deg[son]++,Get_deg(son);
}
void Dijkstra(int type){
    for (int i=0;i<(int)vec[type].size();i++)   Heap.insert(vec[type][i].first,vec[type][i].second);
    while (Heap.tot){
        int Now=Heap.Q[1].x;
        Heap.Delete();
        if (vis[Now])   continue;
        vis[Now]=1;
        for (int p=Rod.now[Now],son=Rod.child[p];p;p=Rod.pre[p],son=Rod.child[p]){
            if (dis[son]>dis[Now]+Rod.val[p]){
                dis[son]=dis[Now]+Rod.val[p];
                Heap.insert(son,dis[son]);
            }
        }
    }
}
void topo(){
    memset(vis,0,sizeof(vis));
    int head=1,tail=1;
    h[1]=col[S],vec[col[S]].push_back(make_pair(S,dis[S]=0));
    for (;head<=tail;head++){
        int Now=h[head];
        Dijkstra(Now);
        for (int p=Pla.now[Now],son=Pla.child[p];p;p=Pla.pre[p],son=Pla.child[p]){
            vec[son].push_back(make_pair(to[p],dis[to[p]]=min(dis[to[p]],dis[from[p]]+Pla.val[p])));//记得取min
            if (!(--deg[son]))  h[++tail]=son;
        }
    }
}
int main(){
    n=read(),m=read(),q=read(),S=read();
    memset(dis,63,sizeof(dis));
    for (int i=1;i<=m;i++){
        int x=read(),y=read(),z=read();
        Rod.insert(x,y,z);
    }
    for (int i=1;i<=n;i++)  if (!col[i])    ++size,dfs(i);
    for (int i=1;i<=q;i++){
        int x=read(),y=read(),z=read();
        Pla.join(col[x],col[y],z),from[Pla.tot]=x,to[Pla.tot]=y;
    }
    Get_deg(col[S]);
    topo();
    for (int i=1;i<=n;i++)  printf(dis[i]>inf?"NO PATH\n":"%d\n",dis[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/Wolfycz/p/9744519.html

时间: 10-04

[Usaco2011 Jan]道路和航线的相关文章

BZOJ2200: [Usaco2011 Jan]道路和航线

n<=25000个点m1<=50000条正权无向边m2<=50000条正负权有向边,保证有向边连接的无向边联通块形成一个拓扑图,求从s到每个点最短路. 第一次发现不会最短路.没看题乱写迪杰无脑WA,很好.迪杰从来不能处理负权最短路,然后就开始啃题解..http://www.cnblogs.com/staginner/archive/2012/10/01/2709487.html这篇代码不错,讲得也很好. 在每个无向边联通块中找最短路可以直接迪杰.至于过度到不同的联通块,可以对无向图联通块

【BZOJ2199】[Usaco2011 Jan]奶牛议会 2-SAT

[BZOJ2199][Usaco2011 Jan]奶牛议会 Description 由于对Farmer John的领导感到极其不悦,奶牛们退出了农场,组建了奶牛议会.议会以“每头牛 都可以获得自己想要的”为原则,建立了下面的投票系统: M只到场的奶牛 (1 <= M <= 4000) 会给N个议案投票(1 <= N <= 1,000) .每只 奶牛会对恰好两个议案 B_i and C_i (1 <= B_i <= N; 1 <= C_i <= N)投 出“是

道路和航线

题目描述 Farmer John正在一个新的销售区域对他的牛奶销售方案进行调查.他想把牛奶送到T个城镇 (1 <= T <= 25,000),编号为1T.这些城镇之间通过R条道路 (1 <= R <= 50,000,编号为1到R) 和P条航线 (1 <= P <= 50,000,编号为1到P) 连接.每条道路i或者航线i连接城镇Ai (1 <= Ai <= T)到Bi (1 <= Bi <= T),花费为Ci.对于道路,0 <= Ci &l

AcWing 道路与航线

AcWing 道路与航线 Description Farmer John正在一个新的销售区域对他的牛奶销售方案进行调查.他想把牛奶送到T个城镇 (1 <= T <= 25,000),编号为1T.这些城镇之间通过R条道路 (1 <= R <= 50,000,编号为1到R) 和P条航线 (1 <= P <= 50,000,编号为1到P) 连接.每条道路i或者航线i连接城镇A_i (1 <= A_i <= T)到B_i (1 <= B_i <= T),

BZOJ 2200 道路与航线

好厉害呀这道题,有种豁然开朗的感觉.... 按拓扑顺序跑最短路. 然后注意细节,像WA的代码犯下的错是一笔带过没有丝毫考虑的...然而就是错了. 考试的时候一定要拍啊. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #include<map> #define

【BZOJ2199】 [Usaco2011 Jan]奶牛议会

Description 由于对Farmer John的领导感到极其不悦,奶牛们退出了农场,组建了奶牛议会.议会以“每头牛 都可以获得自己想要的”为原则,建立了下面的投票系统: M只到场的奶牛 (1 <= M <= 4000) 会给N个议案投票(1 <= N <= 1,000) .每只 奶牛会对恰好两个议案 B_i and C_i (1 <= B_i <= N; 1 <= C_i <= N)投 出“是”或“否”(输入文件中的'Y'和'N').他们的投票结果分别

BZOJ2199 [Usaco2011 Jan]奶牛议会

首先激励一个2-SAT的裸模型,然后发现...tarjan没法判断'?'的情况 于是暴力对每一个议案check一下,直接dfs即可 1 /************************************************************** 2 Problem: 2199 3 User: rausen 4 Language: C++ 5 Result: Accepted 6 Time:160 ms 7 Memory:884 kb 8 ********************

BZOJ 2199: [Usaco2011 Jan]奶牛议会 [2-SAT 判断解]

http://www.lydsy.com/JudgeOnline/problem.php?id=2199 题意:裸的2-SAT,但是问每个变量在所有解中是只能为真还是只能为假还是既可以为真又可以为假 这样的话求$SCC$的做法就不好做了 于是只能用$naive$做法了,枚举每个变量选择真假然后$dfs$一遍看看是否可行 #include <iostream> #include <cstdio> #include <cstring> #include <algori

ACwing 342. 道路与航线

首先看到这道题目,我们发现这道题目的复杂度,首先确定了是O(nlogn)O(nlogn)级别的,所以说,我们的算法初步确定在dijskra和SPFA上面. 但是我们发现这道题目一个关键点,就是题目中出现了负权边. 一旦出现了负权边,那么我们只能使用SPFA. 关于SPFA优化https://www.cnblogs.com/TFLS-gzr/p/10389141.html #include <bits/stdc++.h> using namespace std; const int N=4000