题目
题解&&思路:
自己的思路:
首先这道题可以发现离线是可做的,然后因为自己太菜,没有发现加边更好做,于是一直在考虑删边
删边有点烦,首先对于删去一条边后它及它的祖先的siz都会影响,那么可以使用树剖,每个点记一下siz即可
但是还没完,这样做如果删边时会影响到连通性,因为是离线则一个点的最终祖先(这里表示从当前点一直往上直到不可走为止所到的点)是会变的,sb的我发现这个东西也可以用树剖维护一下,就变得可做了
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int MAXN = 1e5 + 3;
int id[MAXN] , n , m , head[MAXN] , cnt = 1 , inde , top[MAXN] , siz[MAXN] , fa[MAXN];
int dep[MAXN] , son[MAXN] , dfn[MAXN] , zfa[MAXN];
struct node{
int op , x , y;
}ques[MAXN];
struct edge{
int to , nex;
}G[MAXN<<1];
ll ans[MAXN];
char o[3];
void add_edge( int x , int y ){
G[cnt].to = y , G[cnt].nex = head[x];
head[x] = cnt ++;
}
void dfs( int x , int f , int zx ){
zfa[x] = zx;
siz[x] = 1;
for( int i = head[x] ; i ; i = G[i].nex ){
int v = G[i].to;
if( v == f ) continue;
fa[v] = x;
dep[v] = dep[x] + 1;
dfs( v , x , zx );
siz[x] += siz[v];
if( !son[x] || siz[son[x]] < siz[v] ) son[x] = v;
}
}
void dfs1( int x , int f ){
top[x] = f;
id[x] = ++inde;dfn[inde] = x;
if( !son[x] ) return ;
dfs1( son[x] , f );
for( int i = head[x] ; i ; i = G[i].nex ){
int v = G[i].to;
if(fa[x] == v || son[x] == v ) continue;
dfs1( v , v );
}
}
struct tree{
int l , r , lazy1 , zx;
ll sum, lazy;
}tre[MAXN<<2];
int min_( int x , int y ){
if( x == inf ) return y;
if( y == inf ) return x;
if( dep[x] > dep[y] ) return x;
return y;
}
void build( int i , int l , int r ){
tre[i].l = l , tre[i].r = r , tre[i].lazy = 0 , tre[i].sum = 0 , tre[i].lazy1 = inf;
if( l == r ){
tre[i].sum = siz[dfn[l]];tre[i].zx = zfa[dfn[l]];
return ;
}
int mid = l + r >> 1;
build( i << 1 , l , mid );
build( i << 1 | 1 , mid + 1, r );
}
void pushdown( int i ){
if( tre[i].lazy ){
tre[i<<1].sum -= tre[i].lazy , tre[i<<1|1].sum -= tre[i].lazy;
tre[i<<1].lazy += tre[i].lazy , tre[i<<1|1].lazy += tre[i].lazy;
tre[i].lazy = 0;
}
if( tre[i].lazy1 != inf ){
tre[i<<1].zx = min_( tre[i<<1].zx , tre[i].lazy1 ) , tre[i<<1|1].zx = min_( tre[i<<1|1].zx , tre[i].lazy1 );
tre[i<<1].lazy1 = min_( tre[i<<1].lazy1 , tre[i].lazy1 ) , tre[i<<1|1].lazy1 = min_( tre[i<<1|1].lazy1 , tre[i].lazy1 );
tre[i].lazy1 = inf;
}
}
void pushup( int i ){
tre[i].sum = tre[i<<1].sum + tre[i<<1|1].sum;
tre[i].zx = min_( tre[i<<1].zx , tre[i<<1|1].zx );
}
void modify( int i , int l , int r , int delta ){
if( tre[i].l > r || tre[i].r < l ) return ;
if( tre[i].l >= l && tre[i].r <= r ){
tre[i].sum -= delta;
tre[i].lazy += delta;
return ;
}
pushdown( i );
modify( i << 1 , l , r , delta );
modify( i << 1 | 1, l , r , delta );
pushup( i );
}
void modify1( int i , int l, int r , int p ){
if( tre[i].l > r || tre[i].r < l ) return ;
if( tre[i].l >= l && tre[i].r <= r ){
tre[i].zx = min_( tre[i].zx , p );
tre[i].lazy1 = min_( tre[i].lazy1 , p );
return ;
}
pushdown( i );
modify1( i << 1 , l , r , p );
modify1( i << 1 | 1, l , r , p );
pushup( i );
}
ll query( int i , int l , int r ){
if( tre[i].l > r || tre[i].r < l ) return 0;
if( tre[i].l >= l && tre[i].r <= r ){
return tre[i].sum;
}
pushdown( i );
ll su = query( i << 1 , l , r );
su += query( i << 1 | 1, l , r );
pushup( i );
return su;
}
int query1( int i , int l , int r ){
if( tre[i].l > r || tre[i].r < l ) return inf;
if( tre[i].l >= l && tre[i].r <= r ){
return tre[i].zx;
}
pushdown( i );
int su = query1( i << 1 , l , r );
su = min_( su , query1( i << 1 | 1, l , r ) );
pushup( i );
return su;
}
int main(){
// freopen( "path.in" , "r" , stdin );
//freopen( "path.out" , "w" , stdout );
scanf( "%d%d" , &n , &m );
for( int i = 1 ;i <= m; i ++ ){
scanf( "%s" , o );
if( o[0] == 'A' ){
ques[i].op = 1;
scanf( "%d%d" , &ques[i].x , &ques[i].y );
add_edge( ques[i].x , ques[i].y );
add_edge( ques[i].y , ques[i].x );
}
else{
ques[i].op = 0;
scanf( "%d%d" , &ques[i].x , &ques[i].y );
}
}
for( int i = 1 ; i <= n ; i ++ ){
if( !fa[i] ){
dep[i] = 1;
dfs( i , 0 , i );
}
}
for( int i = 1 ; i <= n ; i ++ ){
if( !fa[i] ){
dfs1( i , i );
}
}
build( 1 , 1 , n );
for( int i = m ; i >= 1 ; i -- ){
if( ques[i].op ){
int x = ques[i].x , y = ques[i].y;
if( dep[x] < dep[y] ) swap( x , y );
int p2 = x , p = query( 1 , id[x] , id[x] ) , p1 = query1( 1 , id[x] , id[x] );
x = y;
while( top[x] != top[p1] ){
modify( 1 , id[top[x]] , id[x] , p );
x = fa[top[x]];
}
modify( 1 , id[p1] , id[x] , p );
modify1( 1 , id[p2] , id[p2] + siz[p2] - 1 , p2 );
}
else{
int x = ques[i].x , y = ques[i].y;
if( dep[x] < dep[y] ) swap( x , y );
y = query1( 1 , id[x] , id[x] );
int p = query( 1 , id[y] , id[y] ) ;
int p1 = query( 1 , id[x] , id[x] );
ans[i] = 1ll *( p - p1 ) * p1;
}
}
for( int i = 1 ; i <= m ; i ++ ){
if( !ques[i].op ){
printf( "%lld\n" , ans[i] );
}
}
return 0;
}
代码很长,因为自己太菜了思路太复杂
本身思路是对的,然而自己从没有把树剖一次写对过,这次又挂了一个点,还是同样的modify:
while( top[x] != top[p1] ){
modify( 1 , id[top[x]] , id[x] , p );
x = fa[top[x]];
}
modify( 1 , id[top[p1]] , id[x] , p );
modify1( 1 , id[p2] , id[p2] + siz[p2] - 1 , p2 );
理解起来就是我把modify范围写挂了。。。
之前写错while,这次写错modify,要加强练习啊
其它解:
还是考虑使用树剖,这时按顺序加边,可以使用一个并查集维护连通性,而它的最终祖先是可以通过并查集维护的,就会变得TM好写得多
为了加强自己练习,就把这道题用这种方法再重构一遍:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 1e5 + 3;
int n , m , top[MAXN] , siz[MAXN] , dep[MAXN] , fa[MAXN] , fa1[MAXN] , id[MAXN] , zson[MAXN];
vector<int>G[MAXN];
struct tree{
int l , r;
ll lazy , sum;
}tre[MAXN<<2];
struct node{
int op , x , y;
}q[MAXN];
void dfs( int x , int f ){
siz[x] = 1;
for( int i =0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == f ) continue;
dep[v] = dep[x] + 1;
fa[v] = x;
dfs( v , x );
siz[x] += siz[v];
if( !zson[x] || siz[zson[x]] < siz[v] ) zson[x] = v;
}
}
int inde;
void dfs1( int x , int f ){
top[x] = f;
id[x] = ++inde;
if( !zson[x] ) return ;
dfs1( zson[x] , f );
for( int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == fa[x] || v==zson[x] ) continue;
dfs1( v , v );
}
}
int findSet( int x ){
if( x != fa1[x] ) fa1[x] = findSet( fa1[x] ) ;
return fa1[x];
}
void unionSet( int x , int y ){
int u = findSet( x ) , v = findSet( y );
if( u == v ) return;
if( dep[u] < dep[v] ) fa1[v] = u;
else fa1[u] = v;
}
void pushdown( int i ){
if( tre[i].lazy ){
tre[i<<1].lazy += tre[i].lazy , tre[i<<1|1].lazy += tre[i].lazy;
tre[i<<1].sum += tre[i].lazy , tre[i<<1|1].sum += tre[i].lazy;
tre[i].lazy = 0;
}
}
void build( int i , int l , int r ){
tre[i].l = l , tre[i].r = r , tre[i].lazy = tre[i].sum = 0;
if( l == r){tre[i].sum = 1;return ;}
int mid = l + r >> 1;
build( i << 1 , l ,mid);
build( i << 1 | 1, mid + 1 ,r );
}
void modify( int i , int l , int r , int delta ){
if( tre[i].l > r || tre[i].r < l ) return ;
if( tre[i].l >= l && tre[i].r <= r ){
tre[i].sum += delta;
tre[i].lazy += delta;
return ;
}
pushdown( i );
modify( i << 1 , l , r, delta );
modify( i << 1 | 1 , l , r , delta );
}
int query( int i , int l , int r ){
if( tre[i].l > r || tre[i].r < l ) return 0;
if( tre[i].l >= l && tre[i].r <= r ){
return tre[i].sum;
}
pushdown( i );
int an = query( i << 1 , l , r);
an += query( i << 1 | 1 , l , r);
return an;
}
int main(){
scanf( "%d%d" , &n , &m );
for( int i = 1 ; i <= m ; i ++ ){
char o[3];
scanf( "%s" , o );
if( o[0] == 'A' )
q[i].op = 1;
else q[i].op = 0;
scanf( "%d%d" , &q[i].x , &q[i].y );
if( q[i].op ){
G[q[i].x].push_back( q[i].y );
G[q[i].y].push_back( q[i].x );
}
}
for( int i = 1 ; i <= n ; i ++ ) fa1[i] = i;
for( int i = 1 ;i <= n ; i ++ ){
if( !fa[i]) {
dep[i] = 1;
dfs( i , 0 );
}
}
for( int i = 1 ; i <= n ; i ++) {
if( !fa[i] ){
dfs1( i , i );
}
}
build( 1, 1 , n );
for( int i = 1 ;i <= m ; i++ ){
if( q[i].op ){
int x = q[i].x , y = q[i].y;
if( dep[x] > dep[y] ) swap( x , y );
unionSet( x , y );
int la = findSet( x ) , q = query( 1 , id[y] , id[y] );
while( top[x] != top[la] ){
modify( 1 , id[top[x]] , id[x] , q );
x = fa[top[x]];
}
modify( 1 , id[la] , id[x] , q );
}
else{
int x = q[i].x , y = q[i].y;
if( dep[x] > dep[y] ) swap( x , y );
int la = findSet( x ) , p = query( 1 , id[la] , id[la] );
int p1 = query( 1 , id[y] , id[y] );
printf( "%lld\n" , 1ll * ( p - p1 ) * p1 );
}
}
}
花了25min rush掉了,代码能力还需加强,还不够快
正解:
LCT
再见