1. 程式人生 > >2018.11.03-dtoj-2092-交通 (traffic)

2018.11.03-dtoj-2092-交通 (traffic)

題目描述:

The center of Gdynia is located on an island in the middle of the Kacza river. Every morning thousands of cars drive through this island from the residential districts on the western bank of the river (using bridge connections to junctions on the western side of the island) to the industrial areas on the eastern bank (using bridge connections from junctions on the eastern side of the island).

The island resembles a rectangle, whose sides are parallel to the cardinal directions. Hence, we view it as an A × B rectangle in a Cartesian coordinate system, whose opposite corners are in points (0, 0) and (A,B). On the island, there are n junctions numbered from 1 to n. The junction number i has coordinates (xi, yi). If a junction has coordinates of the form (0, y), it lies on the western side of the island. Similarly, junctions with the coordinates (A, y) lie on the eastern side. Junctions are connected by streets. Each street is a line segment connecting two junctions. Streets can be either unidirectional or bidirectional. No two streets may have a common point (except for, possibly, a common end in a junction). There are are no bridges or tunnels. You should not assume anything else about the shape of the road network. In particular, there can be streets going along the river bank or junctions with no incoming or outgoing streets. Because of the growing traffic density, the city mayor has hired you to check whether the current road network on the island is sufficient.

He asked you to write a program which determines how many junctions on the eastern side of the island are reachable from each junction on the western side.

格丁尼亞的中心位於Kacza河中的一座島嶼。每天清晨,成千上萬輛汽車通過島嶼從西岸的住宅區 (由橋連線島的西部)到東岸的工業區(由橋連線島的東部)。

該島類似於矩形,它的邊平行於主方向。故可將它看作是笛卡爾座標系中的一個A*B的矩形,它的對角分別為(0, 0)和(A, B)。 島上有n個交通節點,編號為1…n(junction, 此處可理解為廣義的路口),第i個節點座標為(xi, yi)。 如果一個節點的座標為(0, y),它就位於島的西岸。類似的,座標為(A, y)的節點位於島的東岸。 各個節點由街道連線,每條街道用線段連線兩個節點。街道有單向行駛或雙向行駛之分。除端點外任意兩條街道都沒有公共點。也沒有橋樑或者隧道。 你不能對道路網路形狀做任何其他假設。沿河岸的街道或節點可能沒有入口或者出口街道。

由於交通堵塞日趨嚴重,市長聘請你測試島上當前的道路網是否足夠。要求你寫一個程式確定從島的西岸的每個節點能夠到達東岸的多少個節點。

輸入:

The first line of the standard input contains four integers n, m, A and B (1 <= n <= 300 000, 0 <= m <= 900 000, 1 <= A,B <= 10^9). They denote the number of junctions in the center of Gdynia, the number of streets and dimensions of the island, respectively. In each of the following n lines there are two integers xi, yi (0 <= xi <= A, 0 <= yi <= B) describing the coordinates of the junction number i. No two junctions can have the same coordinates. The next m lines describe the streets. Each street is represented in a single line by three integers ci, di, ki (1 <= ci, di <= n, ci ≠ di, ki ∈ {1, 2}). Their meaning is that junctions ci and di are connected with a street. If ki = 1, then this is a unidirectional street from ci to di. Otherwise, the street can be driven in both directions. Each unordered pair {ci, di} can appear in the input at most once. You can assume that there is at least one junction on the western side of the island from which it is possible to reach some junction on the eastern side of the island. Additionally, in test cases worth at least 30 points, n,m <= 6 000.

第1行包含4個整數n, m, A, B(1≤n≤300000, 0≤m≤900000,1≤A,B≤10^9),分別表示格丁尼亞市中心的節點數,街道數和島嶼的尺寸。

接下來的n行,每行包含兩個整數xi,yi (0≤xi≤A,0≤yi≤B),表示第i個節點的座標。任意兩個節點的座標都不相同。

再往下的m行表示街道,每條街道用3個整數ci, di, ki(1≤ci, di≤n, ci≠di, ki∈{1,2}),表示節點ci、di有街道連線。如果ki=1,表示從ci到di的街道是單向的,否則,這條街道可以雙向行駛。

每個無序對{ci, di}最多出現1次。

你可以假設西岸節點中至少有1個能夠到達東岸的一些節點。

輸出:

Your program should write to the standard output one line for each junction on the western side of the island. This line should contain the number of junctions on the eastern side that are reachable from that junction. The output should be ordered according to decreasing y-coordinates of the junctions.

為每個西岸節點輸出1行,包括從這個節點出發能夠到達東岸的節點數目,

輸出根據這些節點的y座標降序排序。

資料範圍:

至少有30分的測試資料,n, m≤6000。

1≤n≤300000, 0≤m≤900000,1≤A,B≤10^9

演算法標籤:BFS

思路:

這題的關鍵在於,題目說的不想交是指幾何意義的不相交這意味著,除去不連通的點外,左邊每個點連到右邊一個區間的點,根據這個性質,我們可以除去不連通的點,然後在右邊先從上自下bfs,每個能被第一次遍歷到的點即其最小編號確定,再從下自上遍歷求其最大編號,於是每個點所能達到的右邊區間點的個數幾位maxn-minn+1.共計三遍bfs程式碼長度或速度都優於tarjan

以下程式碼:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=3e5+5,M=9e5+5,inf=1e9;
bool vis[N];
struct node{int y,id;}p1[N],p2[N];
struct data{int op,l,r;}t[M];
int head[N],ne[M<<1],to[M<<1],cnt,tot,val[N];
int n,m,a,b,tot1,tot2,maxn[N],minn[N];
il int read(){int x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return f*x;}
il void insert(int x,int y){ne[++cnt]=head[x];head[x]=cnt;to[cnt]=y;}
bool cmp(node t1,node t2){return t1.y<t2.y;}
bool cmp1(node t1,node t2){return t1.y>t2.y;}
void dfs(int x){
    vis[x]=1;
    for(int i=head[x];i;i=ne[i])
        if(!vis[to[i]])dfs(to[i]);
}
void dfs1(int x,int val){
    minn[x]=val;
    for(int i=head[x];i;i=ne[i]){
        if(!minn[to[i]])dfs1(to[i],val);
    }
}
void dfs2(int x,int val){
    maxn[x]=val;
    for(int i=head[x];i;i=ne[i]){
        if(!maxn[to[i]])dfs2(to[i],val);
    }
}
int main()
{
    n=read();m=read();a=read();b=read();
    for(int i=1;i<=n;i++){
        int x=read(),y=read();
        if(x==0)p1[++tot1]=(node){y,i};
        else if(x==a)p2[++tot2]=(node){y,i};
    }
    for(int i=1;i<=m;i++){
        int x=read(),y=read(),op=read();
        if(op==1)insert(x,y);
        else insert(x,y),insert(y,x);
        t[i].l=x;t[i].r=y;t[i].op=op;
    }
    sort(p2+1,p2+1+tot2,cmp);tot=0;
    for(int i=1;i<=tot1;i++)dfs(p1[i].id);
    for(int i=1;i<=tot2;i++){
        int x=p2[i].id;
        if(!vis[x])continue;
        val[x]=++tot;
    }
    for(int i=1;i<=n;i++)head[i]=0;cnt=0;
    for(int i=1;i<=m;i++){
        if(t[i].op==1){
            insert(t[i].r,t[i].l);
        }
        else{
            insert(t[i].r,t[i].l);
            insert(t[i].l,t[i].r);
        }
    }
    for(int i=1;i<=tot2;i++)if(val[p2[i].id])dfs1(p2[i].id,val[p2[i].id]);
    for(int i=tot2;i;i--)if(val[p2[i].id])dfs2(p2[i].id,val[p2[i].id]);
    sort(p1+1,p1+1+tot1,cmp1);
    for(int i=1;i<=tot1;i++){
        if(maxn[p1[i].id]==0)puts("0");
        else printf("%d\n",maxn[p1[i].id]-minn[p1[i].id]+1);
    }
  return 0;
}
View Code