LCT
和魔法森林类似,先排序一个关键字,用LCT动态维护另一个关键字。
这里把r从大到小排序,然后动态加边。
因为r的顺序是确定的,每次加入新边以后,r肯定是看加入的这条边的,所以我们考虑l就好了。
对于l,我们要求能够通过的数量,肯定是要保留合法的大范围,因为小范围包含在了大范围里。
之后就和魔法森林一样维护1~n的链。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define full(a, b) memset(a, b, sizeof a)
#define __fastIn ios::sync_with_stdio(false), cin.tie(0)
#define pb push_back
using namespace std;
typedef long long LL;
inline int lowbit(int x){ return x & (-x); }
inline int read(){
int ret = 0, w = 0; char ch = 0;
while(!isdigit(ch)){
w |= ch == '-', ch = getchar();
}
while(isdigit(ch)){
ret = (ret << 3) + (ret << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -ret : ret;
}
template <typename A>
inline A __lcm(A a, A b){ return a / __gcd(a, b) * b; }
template <typename A, typename B, typename C>
inline A fpow(A x, B p, C lyd){
A ans = 1;
for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
return ans;
}
const int N = 200005;
int n, m, tot, ch[N][2], fa[N], rev[N], mx[N], id[N], st[N], w[N];
struct Edge{
int u, v, l, r;
bool operator < (const Edge &rhs) const {
return r > rhs.r;
}
}e[N];
int build(int val){
++ tot;
w[tot] = mx[tot] = val, id[tot] = tot;
ch[tot][0] = ch[tot][1] = rev[tot] = fa[tot] = 0;
return tot;
}
bool isRoot(int x){
return ch[fa[x]][0] != x && ch[fa[x]][1] != x;
}
void push_up(int rt){
int l = ch[rt][0], r = ch[rt][1];
id[rt] = rt, mx[rt] = w[rt];
if(mx[l] > mx[rt]) mx[rt] = mx[l], id[rt] = id[l];
if(mx[r] > mx[rt]) mx[rt] = mx[r], id[rt] = id[r];
}
void reverse(int x){
rev[x] ^= 1;
swap(ch[x][0], ch[x][1]);
}
void push_down(int x){
if(rev[x]){
int l = ch[x][0], r = ch[x][1];
if(l) reverse(l);
if(r) reverse(r);
rev[x] ^= 1;
}
}
void rotate(int x){
int y = fa[x], z = fa[y], p = (ch[y][1] == x) ^ 1;
ch[y][p ^ 1] = ch[x][p], fa[ch[x][p]] = y;
if(!isRoot(y)) ch[z][ch[z][1] == y] = x;
fa[x] = z, fa[y] = x, ch[x][p] = y;
push_up(y), push_up(x);
}
void splay(int x){
int pos = 0; st[++ pos] = x;
for(int i = x; !isRoot(i); i = fa[i]) st[++ pos] = fa[i];
while(pos) push_down(st[pos --]);
while(!isRoot(x)){
int y = fa[x], z = fa[y];
if(!isRoot(y)){
(ch[y][0] == x) ^ (ch[z][0] == y) ? rotate(x) : rotate(y);
}
rotate(x);
}
push_up(x);
}
void access(int x){
for(int p = 0; x; p = x, x = fa[x])
splay(x), ch[x][1] = p, push_up(x);
}
void makeRoot(int x){
access(x), splay(x), reverse(x);
}
void link(int x, int y){
makeRoot(x), fa[x] = y;
}
int findRoot(int x){
access(x), splay(x);
while(ch[x][0]) push_down(x), x = ch[x][0];
splay(x);
return x;
}
void split(int x, int y){
makeRoot(x), access(y), splay(y);
}
bool isConnect(int x, int y){
makeRoot(x);
return findRoot(y) == x;
}
vector<pair<int, int>> ans;
int main(){
n = read(), m = read();
for(int i = 1; i <= m; i ++){
e[i].u = read(), e[i].v = read(), e[i].l = read(), e[i].r = read();
}
for(int i = 1; i <= n; i ++) build(0);
sort(e + 1, e + m + 1);
for(int i = 1; i <= m; i ++){
int u = e[i].u, v = e[i].v, t = build(e[i].l);
if(!isConnect(u, v)){
link(u, t), link(t, v);
}
else{
split(u, v);
if(mx[v] >= e[i].l){
int p = id[v];
splay(p);
fa[ch[p][0]] = 0, fa[ch[p][1]] = 0;
ch[p][0] = ch[p][1] = 0;
link(u, t), link(t, v);
}
}
if(isConnect(1, n)){
split(1, n);
if(mx[n] <= e[i].r) ans.pb(make_pair(mx[n], e[i].r));
}
}
sort(ans.begin(), ans.end());
int res = 0;
for(int i = 0; i < ans.size(); i ++){
int j = i, cur = ans[i].second;
while(j + 1 < ans.size() && ans[j + 1].first <= cur) cur = max(cur, ans[++ j].second);
res += cur - ans[i].first + 1;
i = j;
}
printf("%d
", res);
return 0;
}