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

输出描述:

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
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 pii;
#define mp(x,y) make_pair(x,y)
vectort[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)
{
    vectortmp;
    for(int i=0; i 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>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<

 

你可能感兴趣的:(线段树分治)