模拟28 —「遗忘之祭仪 · 客星璀璨之夜 · 割海成路之日」
遗忘之祭仪
按照题意模拟,因为如果当前不能完全匹配并且消除,之后也不能。
客星璀璨之夜
首先先用期望的线性性展开成每一条边期望经过的次数*长度。
问题就在于求这个期望经过次数。
设 \(dp_{i,j}\) 表示有i个反物质星的时候,第j条路径的期望经过次数。
转移分情况讨论.
选择一条 \([1,j-1]\) 的边 ,就转移 \(dp_{i-1,j-2}\)
选择一条 \(j\) 的边,就转移 \(dp_{i-1,j-1}\)
选择一条 \([j+1,2*i]\) 的边,就转移 \(dp_{i-1,j}\)
这些都是从i-1的情况转移过来的(也就是在i个球的时候没有选择第j条边)。
如果在第i个球的时候选择第j条边,那么剩下的随便选,即(\(2^{i-1}*(i-1)!\))。
所以转移方程是
\(dp_{i,j}=dp_{i-1,j-2}*(j-1)+dp_{i-1,j-1}+dp_{i-1,j}*(2*i-j)+(2^{i-1}*(i-1)!)\)
最后在乘上总情况(\(2^n*n!\))的逆元就行了。
割海成路之日
一道非常巧妙的并查集应用,考场上我都没有看出来是并查集。
首先,这道题的一大特点是道路只会变浅,不会变深,这也许其实我们可以用不用支持撤销的数据结构来解题(所以是并查集?)。
然后转化成从s出发,可以到达多少点,只不过条件变成了先经过1边,再经过3,最后经过1,2。
先来解决第一问,两个点能够相互到达,就是从t的1,2出发,从s的1出发,有个有公共点或者通过一条3边。
这个时候,在树上维护两个并查集,一个是一个点出发通过1边到达的所有点,另一个是一个点通过1,2边到达的所有点。
设第一个并查集是 fa1 , 第二个是fa2,x点的父亲是FA(x).
我们额外维护,让并查集的根是深度最小的点(或者额外维护一个并查集中深度最小的点是谁这个信息也可以)
那么,t可以到达s,当且进当满足如下条件之一。
- \(fa2(s)=fa2(t)\) 这样仅通过1,2可以到达。
- \(fa1(FA(fa2(t)))=fa1(s)\) 这样是t在s下面,通过t的1,2,跳一条边,再通过1可以互达
- \(fa2(FA(fa1(s)))=fa2(t)\) 同理,是s在t下面。
若s,t的lca不是s和t中的一个,情况2,3等价
然后比较麻烦的是第二问。
答案由两部分组成
- 直接通过1,2到达。
- 通过1,通过3后通过1,2到达。
第一种直接对第二个并查集维护size就可以了,记为size2。
第二种,需要对第一个并查集维护所有点向下,通过一个3边,能到达的2并查集size之和,记为size1。
查答案直接size1+size2+通过父亲到达的点(因为size1只维护了向下的点),(维护向上很难维护,不过也可以维护)
2变1,直接合并1的并查集就行了。
3变2,合并2的,同时对1的修改修改,这个不难讨论,就是情况不少。
然后差不多做完了,有一个实现的小技巧。
一开始把所有边都视作3边,如果这条边是1,就看做变2再变1,是2同理。
我的实现有点麻烦,边的类型不用map存,可以直接用并查集判断边的关系。
code
#include <map>
#include <cstdio>
#include <cstring>
#include <assert.h>
#include <algorithm>
#define R register int
#define scanf Ruusupuu = scanf
#define freopen rsp_5u = freopen
#define fre(x) freopen( #x".in" , "r" , stdin ) , freopen( #x".out" , "w" , stdout )
int Ruusupuu ;
FILE * rsp_5u ;
using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 3e5 + 10 ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ 48 ) , ch = getchar() ;
return fg ? -w : w ;
}
map< pair< int , int > , int > mp ;
int n , m , x , y , z , s , t , dep [N] , fa [N] , fa1 [N] , fa2 [N] , siz1 [N] , siz2 [N] ;
int head [N] , to [N << 1] , net [N << 1] , fr [N << 1] , w [N << 1] , cnt = 1 ;
#define add( f , t ) fr [++ cnt] = f , to [cnt] = t , net [cnt] = head [f] , head [f] = cnt , w [cnt] = z
void sc(){
n = read() , m = read() , memset( head , -1 , sizeof( head ) ) ;
for( R i = 1 ; i < n ; i ++ ){
x = read() , y = read() , z = read() , add( x , y ) , add( y , x ) ;
if( x > y ) swap( x , y ) ;
mp [make_pair( x , y )] = z ;
} for( R i = 1 ; i <= n ; i ++ ) fa1 [i] = fa2 [i] = i , siz2 [i] = 1 ;
}
int fd1( int x ){
if( x == fa1 [x] ) return x ;
else return fa1 [x] = fd1( fa1 [x] ) ;
}
int fd2( int x ){
if( x == fa2 [x] ) return x ;
else return fa2 [x] = fd2( fa2 [x] ) ;
}
void un1( int x , int y ){
if( dep [x] > dep [y] ) swap( x , y ) ;
x = fd1( x ) , y = fd1( y ) ;
if( x == y ) return ;
fa1 [y] = x , siz1 [x] += siz1 [y] ;
}
void un2( int x , int y ){
if( dep [x] > dep [y] ) swap( x , y ) ;
x = fd2( x ) , y = fd2( y ) ;
if( x == y ) return ;
fa2 [y] = x , siz2 [x] += siz2 [y] ;
siz1 [fd1( fa [y] )] -= siz2 [y] ;
if( fd1( fa [x] ) ) siz1 [fd1( fa [x] )] += siz2 [y] ;
}
void dfs( int x , int Fa ){
for( R i = head [x] ; ~i ; i = net [i] ){
int y = to [i] ;
if( y == Fa ) continue ;
fa [y] = x , dep [y] = dep [x] + 1 ;
dfs( y , x ) ;
siz1 [x] ++ ;
if( w [i] <= 2 ) un2( x , y ) ;
if( w [i] == 1 ) un1( x , y ) ;
}
}
inline bool check( int x , int y ){
if( fd2( x ) == fd2( y ) ) return 1 ;
if( fd2( fa [fd1(x)] ) == fd2( y ) ) return 1 ;
if( fd1( fa [fd2(y)] ) == fd1( x ) ) return 1 ;
return 0 ;
}
inline int count( int ix ){
int x = fd1( ix ) , y = fa [fd1( x )] ;
if( x > y ) swap( x , y ) ;
bool fg = ( mp [make_pair( x , y )] == 3 ) ;
return siz2 [fd2( ix )] + siz1 [fd1( ix )] + ( fg ? siz2 [fd2( fa [fd1( ix )] )] : 0 ) ;
}
void work(){
dfs( 1 , 0 ) ;
while( m -- ){
x = read() , y = read() , s = read() , t = read() ;
if( x > y ) swap( x , y ) ;
int zt = mp [make_pair( x , y )] ;
if( zt == 2 ) mp [make_pair( x , y )] = 1 , un1( x , y ) ;
else if( zt == 3 ) mp [make_pair( x , y )] = 2 , un2( x , y ) ;
printf( "%d %d\n" , check( s , t ) , count( s ) ) ;
}
}
signed main(){
sc() ;
work() ;
return 0 ;
}

浙公网安备 33010602011771号