链接: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]
当然最后一个其实用不到。
这样的话上述区间就可以刚好用若干个点表示。
*/