题面
题意
就是给你一张无向图,如果需要通过该条边,则
s
i
z
e
size
s i z e 值的取值区间为
[
l
,
r
]
[l,r]
[ l , r ] ,问你从
1
1
1 走到
n
n
n 有多少种可行的
s
i
z
e
size
s i z e .
题解
线段树分治(时间分治)模板题 线段树分治考虑的是统计每一个操作可以影响的时间区间,然后用线段树去维护这些影响的区间,最后做一次
d
f
s
dfs
d f s 统计所有询问的答案,对于这题,显然想到的是考虑每一条边的添加所产生的
s
i
z
e
size
s i z e 区间贡献,也就是说,应该将
s
i
z
e
size
s i z e 区间建一颗线段树,当然需要离散化一下,然后跑线段树分治,到达某个叶子结点时,如果可撤销并查集中
1
1
1 与
n
n
n 在同一个几何中,则当前节点对应的区间加到答案中 另外一个做法是
L
C
T
LCT
L C T ,和BZOJ3669 的做法很相似,这题也是每条边相当于有两个属性
l
l
l 和
r
r
r ,即将所有边按照
r
r
r 降序排序,然后去维护
l
l
l 的最小生成树,然后查询的时候就是查询路径上最大边权作为可行答案的左区间,右区间即为当前枚举的区间右端点
线段树分治代码
#include <bits/stdc++.h>
using namespace std;
const int maxn= 2e5 + 10 ;
const int maxm= 2e5 + 10 ;
int n, m, le[ maxn] , ri[ maxn] , b[ maxn] , cnt;
struct operate{
int x, y, l, r;
operate ( int a= 0 , int b= 0 , int c= 0 , int d= 0 ) {
x= a; y= b; l= c; r= d;
}
} o[ maxm] , sta[ maxm] ;
namespace dsu{
int fa[ maxn] , dep[ maxn] , tot;
void init ( int k) {
tot= 0 ;
for ( int i= 1 ; i<= k; i++ ) fa[ i] = i, dep[ i] = 0 ;
}
int find ( int x) {
return fa[ x] == x? x: find ( fa[ x] ) ;
}
void merge ( int x, int y) {
x= find ( x) , y= find ( y) ;
if ( x== y) return ;
if ( dep[ x] > dep[ y] ) swap ( x, y) ;
if ( dep[ x] == dep[ y] ) dep[ y] ++ ;
sta[ ++ tot] = operate ( x, y, 0 , 0 ) ;
fa[ x] = y;
}
void undo ( int id) {
while ( tot> id) {
int x= sta[ tot] . x, y= sta[ tot-- ] . y;
if ( dep[ y] == dep[ x] + 1 ) dep[ y] -- ;
fa[ x] = x;
}
}
} ;
using namespace dsu;
namespace segment_tree{
vector< operate> add[ maxm<< 2 ] ;
void update ( int id, int L, int R, int l, int r, int k) {
if ( le[ L] >= l&& ri[ R] <= r) { add[ id] . push_back ( o[ k] ) ; return ; }
int mid= ( L+ R) >> 1 ;
if ( l<= ri[ mid] ) update ( id<< 1 , L, mid, l, r, k) ;
if ( r> ri[ mid] ) update ( id<< 1 | 1 , mid+ 1 , R, l, r, k) ;
}
int work ( int id, int L, int R) {
int now= tot, ans= 0 ;
for ( int i= 0 ; i< add[ id] . size ( ) ; i++ ) merge ( add[ id] [ i] . x, add[ id] [ i] . y) ;
if ( L== R) {
if ( find ( 1 ) == find ( n) ) ans+ = ( ri[ R] - le[ L] + 1 ) ;
} else {
int mid= ( L+ R) >> 1 ;
ans+ = work ( id<< 1 , L, mid) ; ans+ = work ( id<< 1 | 1 , mid+ 1 , R) ;
}
undo ( now) ;
return ans;
}
} ;
using namespace segment_tree;
int main ( )
{
scanf ( "%d %d" , & n, & m) ;
init ( n) ;
for ( int i= 1 ; i<= m; i++ ) {
scanf ( "%d %d %d %d" , & o[ i] . x, & o[ i] . y, & o[ i] . l, & o[ i] . r) ;
b[ ++ cnt] = o[ i] . l; b[ ++ cnt] = o[ i] . r+ 1 ;
}
sort ( b+ 1 , b+ cnt+ 1 ) ;
int num= unique ( b+ 1 , b+ cnt+ 1 ) - b- 1 ;
for ( int i= 1 ; i< num; i++ ) le[ i] = b[ i] , ri[ i] = b[ i+ 1 ] - 1 ;
for ( int i= 1 ; i<= m; i++ ) {
update ( 1 , 1 , num- 1 , o[ i] . l, o[ i] . r, i) ;
}
printf ( "%d\n" , work ( 1 , 1 , num- 1 ) ) ;
}
L
C
T
LCT
L C T 代码
#include <bits/stdc++.h>
using namespace std;
const int maxn= 2e5 + 10 ;
#define inf 0x3f3f3f3f
struct node{
int x, y, l, r;
node ( int a= 0 , int b= 0 , int c= 0 , int d= 0 ) {
x= a; y= b; l= c; r= d;
}
friend bool operator< ( const node& a, const node& b) {
if ( a. r== b. r) return a. l> b. l;
return a. r> b. r;
}
} o[ maxn] ;
namespace LCT{
int ch[ maxn] [ 2 ] , fa[ maxn] , sta[ maxn] , mark[ maxn] ;
long long val[ maxn] , ans[ maxn] , sum[ maxn] ;
inline bool not_root ( int x) {
return ch[ fa[ x] ] [ 0 ] == x|| ch[ fa[ x] ] [ 1 ] == x;
}
inline int dir ( int x) {
return ch[ fa[ x] ] [ 1 ] == x;
}
inline void add_mark ( int x) {
swap ( ch[ x] [ 0 ] , ch[ x] [ 1 ] ) ;
mark[ x] ^ = 1 ;
}
inline void push_down ( int x) {
if ( mark[ x] ) {
if ( ch[ x] [ 0 ] ) add_mark ( ch[ x] [ 0 ] ) ;
if ( ch[ x] [ 1 ] ) add_mark ( ch[ x] [ 1 ] ) ;
mark[ x] = 0 ;
}
}
inline void push_up ( int x) {
ans[ x] = x;
if ( val[ ans[ ch[ x] [ 0 ] ] ] > val[ ans[ x] ] ) ans[ x] = ans[ ch[ x] [ 0 ] ] ;
if ( val[ ans[ ch[ x] [ 1 ] ] ] > val[ ans[ x] ] ) ans[ x] = ans[ ch[ x] [ 1 ] ] ;
}
inline void pushall ( int x) {
if ( not_root ( x) ) pushall ( fa[ x] ) ;
push_down ( x) ;
}
inline void rotate ( int x) {
int y= fa[ x] , z= fa[ y] , k= dir ( x) ;
if ( ch[ x] [ k^ 1 ] ) fa[ ch[ x] [ k^ 1 ] ] = y; ch[ y] [ k] = ch[ x] [ k^ 1 ] ;
if ( not_root ( y) ) ch[ z] [ dir ( y) ] = x; fa[ x] = z;
ch[ x] [ k^ 1 ] = y; fa[ y] = x;
push_up ( y) ; push_up ( x) ;
}
inline void splay ( int x, int goal= 0 ) {
pushall ( x) ;
while ( not_root ( x) ) {
int y= fa[ x] , z= fa[ y] ;
if ( not_root ( y) ) {
if ( dir ( x) == dir ( y) ) rotate ( y) ;
else rotate ( x) ;
}
rotate ( x) ;
}
}
inline void access ( int x) {
for ( int y= 0 ; x; y= x, x= fa[ x] ) {
splay ( x) ; ch[ x] [ 1 ] = y; push_up ( x) ;
}
}
inline void make_root ( int x) {
access ( x) ; splay ( x) ; add_mark ( x) ;
}
inline int find_root ( int x) {
access ( x) ; splay ( x) ;
while ( ch[ x] [ 0 ] ) push_down ( x) , x= ch[ x] [ 0 ] ;
splay ( x) ;
return x;
}
inline void split ( int x, int y) {
make_root ( x) ; access ( y) ; splay ( y) ;
}
inline bool link ( int x, int y) {
make_root ( x) ;
if ( find_root ( y) == x) return 0 ;
fa[ x] = y; return 1 ;
}
inline bool cut ( int x, int y) {
make_root ( x) ;
if ( find_root ( y) != x|| fa[ y] != x|| ch[ y] [ 0 ] ) return 0 ;
fa[ y] = ch[ x] [ 1 ] = 0 ;
push_up ( x) ;
return 1 ;
}
inline long long query_max ( int l, int r) {
split ( l, r) ;
return ans[ r] ;
}
inline long long query_sum ( int l, int r) {
split ( l, r) ;
return sum[ r] ;
}
} ;
using namespace LCT;
vector< pair< int , int > > res;
int n, m;
int main ( )
{
scanf ( "%d %d" , & n, & m) ;
for ( int i= 1 ; i<= m; i++ ) scanf ( "%d %d %d %d" , & o[ i] . x, & o[ i] . y, & o[ i] . l, & o[ i] . r) ;
sort ( o+ 1 , o+ m+ 1 ) ;
for ( int i= 1 ; i<= m; i++ ) {
if ( find_root ( o[ i] . x) == find_root ( o[ i] . y) ) {
int x= query_max ( o[ i] . x, o[ i] . y) ;
if ( o[ x- n] . l> o[ i] . l) {
cut ( x, o[ x- n] . x) , cut ( x, o[ x- n] . y) ;
val[ n+ i] = o[ i] . l; link ( n+ i, o[ i] . x) , link ( n+ i, o[ i] . y) ;
}
} else {
val[ n+ i] = o[ i] . l; link ( n+ i, o[ i] . x) ; link ( n+ i, o[ i] . y) ;
}
if ( find_root ( 1 ) == find_root ( n) ) {
int x= query_max ( 1 , n) ;
if ( o[ x- n] . l<= o[ i] . r) res. push_back ( make_pair ( o[ x- n] . l, o[ i] . r) ) ;
}
}
sort ( res. begin ( ) , res. end ( ) ) ;
int tot= res. size ( ) ; long long ans= 0 ;
for ( int i= 0 ; i< tot; ) {
int j= i, maxx= res[ i] . second;
while ( j+ 1 < tot&& res[ j+ 1 ] . first<= maxx) {
maxx= max ( maxx, res[ j+ 1 ] . second) ;
j++ ;
}
ans+ = 1LL * ( maxx- res[ i] . first+ 1 ) ;
i= j+ 1 ;
}
printf ( "%lld\n" , ans) ;
}