LCT
和魔法森林类似,先排序一个关键字,用LCT动态维护另一个关键字。
这里把r从大到小排序,然后动态加边。
因为r的顺序是确定的,每次加入新边以后,r肯定是看加入的这条边的,所以我们考虑l就好了。
对于l,我们要求能够通过的数量,肯定是要保留合法的大范围,因为小范围包含在了大范围里。
之后就和魔法森林一样维护1~n的链。
#include#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_backusing 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 inline A __lcm(A a, A b){ return a / __gcd(a, b) * b; }template 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 > 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\n", res); return 0;}