2019牛客暑期多校訓練營(第八場) Explorer (線段樹分治+區間離散化)

鏈接:https://ac.nowcoder.com/acm/contest/888/E
來源:牛客網
 

Explorer

時間限制:C/C++ 2秒,其他語言4秒
空間限制:C/C++ 524288K,其他語言1048576K
64bit IO Format: %lld

題目描述

Gromah and LZR have entered the fifth level. Unlike the first four levels, they should do some moves in this level.

 

There are nn_{}n​ vertices and mm_{}m​ bidirectional roads in this level, each road is in format (u,v,l,r)(u, v, l, r)_{}(u,v,l,r)​, which means that vertex uu_{}u​ and vv_{}v​ are connected by this road, but the sizes of passers should be in interval [l,r][l, r]_{}[l,r]​. Since passers with small size are likely to be attacked by other animals and passers with large size may be blocked by some narrow roads.

 

Moreover, vertex 11_{}1​ is the starting point and vertex nn_{}n​ is the destination. Gromah and LZR should go from vertex 11_{}1​ to vertex nn_{}n​ to enter the next level.

 

At the beginning of their exploration, they may drink a magic potion to set their sizes to a fixed positive integer. They want to know the number of positive integer sizes that make it possible for them to go from 11_{}1​ to nn_{}n​.

 

Please help them to find the number of valid sizes.
 

輸入描述:


 

The first line contains two positive integers n,mn,m_{}n,m​, denoting the number of vertices and roads.

 

Following m lines each contains four positive integers u,v,l,ru, v, l, r_{}u,v,l,r​, denoting a bidirectional road (u,v,l,r)(u, v, l, r)_{}(u,v,l,r)​.

 

 

1≤n,m≤105,1≤u<v≤n,1≤l≤r≤1091 \le n,m \le 10^5, 1 \le u < v \le n, 1 \le l \le r \le 10^91≤n,m≤105,1≤u<v≤n,1≤l≤r≤109

輸出描述:

Print a non-negative integer in a single line, denoting the number of valid sizes.

示例1

輸入

複製

5 5
1 2 1 4
2 3 1 2
3 5 2 4
2 4 1 3
4 5 3 4

輸出

複製

2

說明


 

There are 2 valid sizes : 2 and 3.

 

For size 2, there exists a path 1→2→3→51 \rightarrow 2 \rightarrow 3 \rightarrow 51→2→3→5.

 

For size 3, there exists a path 1→2→4→51 \rightarrow 2 \rightarrow 4 \rightarrow 51→2→4→5.

題目大意:給了一個無向圖,n個點,m條邊,每一條邊有一個上下限設位高度,有多少個高度和以從

1到n,可以通過一條邊的條件爲這個高度在這條邊的範圍內。

解題思路:由於區間範圍較大,所以需要對區間離散化,對於一些邊,他能影響到的區間我們可以分爲logn個區間。

我們將這些邊加入到這些區間。然後我們可以遍歷一遍我們的線段樹,對於當前的區間,我們用並查集維護一下連通性。

如果當前的1-n是聯通的話,當前的區間就可以作爲答案。但是這裏的並查集要撤銷,所以不能路徑壓縮。

同時合併的時候要按秩合併。

#include<bits/stdc++.h>
using namespace std;
#define pb(x) push_back(x)
typedef long long ll;
const int mmax = 1e5+5;
const int N = 2e5+5;

int fa[N],hi[N];
struct node
{
    int x,y,l,r;
}a[N];

int n;
typedef pair<int,int> pii;
#define mp(x,y) make_pair(x,y)
vector<int>t[N*4];

int Find(int x)
{
    while(x!=fa[x]) x = fa[x];
    return x;
}

void upd(int rt,int l,int r,int ql,int qr,int k)
{
    if(l>=ql && r<=qr)
    {
        t[rt].pb(k);
        return ;
    }
    int m = (l+r)>>1;
    if(ql <= m) upd(rt<<1,l,m,ql,qr,k);
    if(qr > m) upd(rt<<1|1,m+1,r,ql,qr,k);
}

struct rec
{
    int l,r,del;
    rec(int x,int y,int z)
    {
        l = x, r = y,del = z;
    }
};

int ans;
int b[N];

void ask(int rt,int l,int r)
{
    vector<rec>tmp;
    for(int i=0; i<t[rt].size(); i++)
    {
        int id = t[rt][i];
        int fx = Find(a[id].x);
        int fy = Find(a[id].y);
        if(fx==fy)continue;
        if(hi[fx] > hi[fy])swap(fx,fy);///hi指的是樹高。
        fa[fx] = fy;                  ///小的合併到大的。
        tmp.pb(rec(fx,fy,hi[fy]));
        hi[fy] = max(hi[fx]+1,hi[fy]);
    }

    if(Find(1) == Find(n))
    {
         ans += b[r+1] - b[l]; ///之前這裏寫的return。但是忘了還原並查集了。。。
    }
    else if(l<r){
    int m  = (l+r)>>1;
    ask(rt<<1,l,m);
    ask(rt<<1|1,m+1,r);
    }

    for(int i=tmp.size()-1; i>=0; i--)
    {
        rec now = tmp[i];
        fa[now.l] = now.l;
        hi[now.r] -= now.del;
    }
}

int main()
{
    int m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)fa[i] = i ,hi[i] = 0;
    int tot=0;

    for(int i=1; i<=m; i++)
    {
        scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].l,&a[i].r);
        b[++tot] = a[i].l;
        b[++tot] = a[i].r+1; ///r+1,是爲了用若干個葉子節點表示區間。
    }
    sort(b+1,b+1+tot);
    tot = unique(b+1, b+1+tot)-b-1;

    for(int i=1; i<=m; i++)
    {
        int l = lower_bound(b+1,b+1+tot,a[i].l)-b;
        int r = lower_bound(b+1,b+1+tot,a[i].r+1)-b;
        upd(1,1,tot,l,r-1,i); ///這裏是r-1。
    }

    ans = 0;
    ask(1,1,tot);
    cout<<ans<<endl;
}
/*
對於上述離散化區間的方法:
對於每一個點r++是爲了處理方便。
例如:[1,4] [3,10] [1,1].
如果讓r++的話
1 3 4 10
這樣的話4個葉子節點無法表示上述區間。。
r++之後
1 2 3 5 11
這樣的話5個葉子節點可以分別表示5個區間
1:[1,1]
2: [2,2]
3: [3,4]
4: [5,10]
5: [11,11]
當然最後一個其實用不到。
這樣的話上述區間就可以剛好用若干個點表示。
*/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章