Description
有一个
m
m
m 行
n
n
n 列的图,第
i
i
i 列有连向
i
+
1
i+1
i + 1 列的有向边,有
q
q
q 次操作:
修改
i
i
i 列
j
j
j 行向
i
+
1
i+1
i + 1 列
k
k
k 行的边的状态。 对于
l
,
r
l,r
l , r ,给出
l
,
r
l,r
l , r 列的起点和终点的状态,求最少删除多少个点可以使得不存在一个起点能够到达任意一个终点。
m
≤
24
,
n
,
q
≤
8292
m\le24,n,q\le8292
m ≤ 2 4 , n , q ≤ 8 2 9 2
Solution
暴力可以最小割。 考虑最小割=最大流,而每一个点只能割一次,因此求的实际上是最大不相交路径个数。 考虑LGV定理,对于两两的路径数建一个矩阵,求它的行列式就是带符号的起点终点两两匹配且不相交的路径个数。由于它是带符号的,因此我们需要给每一条边随机权值,在对一个
1
e
9
1e9
1 e 9 级别的数取模意义下,刚好消成0的概率是
1
m
o
\frac{1}{mo}
m o 1 ,(不要用
u
n
s
i
g
n
e
d
i
n
t
unsigned\ int
u n s i g n e d i n t ,容易出锅). 问题是我们怎么求最大的匹配数
c
c
c 呢?注意到如果存在一个
c
∗
c
c*c
c ∗ c 子矩阵行列式不为0,而
(
c
+
1
)
∗
(
c
+
1
)
(c+1)*(c+1)
( c + 1 ) ∗ ( c + 1 ) 的所有子矩阵都为0,那么
c
c
c 实际上就是矩阵的秩,因此我们只需要求矩阵的秩就好了。 用线段树、矩阵乘法维护路径数,需要注意取模和常数。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <ctime>
#define maxn 8205
#define maxm 25
#define ll long long
#define mo 9999991
using namespace std;
int n, m, q, i, j, k, a[ maxn] [ maxm] [ maxm] , d1[ maxm] , d2[ maxm] ;
int rd ( ) { return ( rand ( ) * rand ( ) + rand ( ) ) % ( mo- 1 ) + 1 ; }
struct matrix{ ll a[ maxm] [ maxm] ; } t[ maxn* 4 ] , S;
matrix operator* ( matrix A, matrix B) {
matrix C; memset ( C. a, 0 , sizeof ( C. a) ) ;
for ( int i= 0 ; i< m; i++ ) for ( int j= 0 ; j< m; j++ ) for ( int k= 0 ; k< m; k++ )
C. a[ i] [ k] + = A. a[ i] [ j] * B. a[ j] [ k] ;
for ( int i= 0 ; i< m; i++ ) for ( int j= 0 ; j< m; j++ ) C. a[ i] [ j] = C. a[ i] [ j] % mo;
return C;
}
void maketree ( int x, int l, int r) {
if ( l== r) {
for ( int i= 0 ; i< m; i++ ) for ( int j= 0 ; j< m; j++ ) if ( a[ l] [ i] [ j] )
t[ x] . a[ i] [ j] = rd ( ) ; else t[ x] . a[ i] [ j] = 0 ;
return ;
}
int mid= ( l+ r) >> 1 ;
maketree ( x<< 1 , l, mid) , maketree ( x<< 1 ^ 1 , mid+ 1 , r) ;
t[ x] = t[ x<< 1 ] * t[ x<< 1 ^ 1 ] ;
}
void change ( int x, int l, int r, int p) {
if ( l== r) {
for ( int i= 0 ; i< m; i++ ) for ( int j= 0 ; j< m; j++ ) if ( a[ l] [ i] [ j] )
t[ x] . a[ i] [ j] = rd ( ) ; else t[ x] . a[ i] [ j] = 0 ;
return ;
}
int mid= ( l+ r) >> 1 ;
if ( p<= mid) change ( x<< 1 , l, mid, p) ; else change ( x<< 1 ^ 1 , mid+ 1 , r, p) ;
t[ x] = t[ x<< 1 ] * t[ x<< 1 ^ 1 ] ;
}
void find ( int x, int l, int r, int L, int R) {
if ( l> R|| r< L) return ;
if ( L<= l&& r<= R) {
S= S* t[ x] ;
return ;
}
int mid= ( l+ r) >> 1 ;
find ( x<< 1 , l, mid, L, R) , find ( x<< 1 ^ 1 , mid+ 1 , r, L, R) ;
}
ll A[ maxm] [ maxm] ;
ll ksm ( ll x, ll y) {
ll s= 1 ;
for ( ; y; y/ = 2 , x= x* x% mo) if ( y& 1 )
s= s* x% mo;
return s;
}
int getrk ( int n, int m) {
static ll tmp[ maxm] [ maxm] ;
if ( n> m) {
for ( i= 0 ; i< n; i++ ) for ( j= 0 ; j< m; j++ )
tmp[ j] [ i] = A[ i] [ j] ;
memcpy ( A, tmp, sizeof ( A) ) ;
swap ( n, m) ;
}
int c= 0 ;
for ( i= 0 ; i< m&& c< n; i++ ) {
for ( j= c; j< n; j++ ) if ( A[ j] [ i] ) break ;
if ( j== n) continue ;
for ( k= 0 ; k< m; k++ ) swap ( A[ c] [ k] , A[ j] [ k] ) ;
ll inv= ksm ( A[ c] [ i] , mo- 2 ) ;
for ( j= 0 ; j< m; j++ ) A[ c] [ j] = A[ c] [ j] * inv% mo;
for ( j= 0 ; j< n; j++ ) if ( c!= j) for ( k= m- 1 ; k>= i; k-- )
( A[ j] [ k] - = A[ j] [ i] * A[ c] [ k] ) % = mo;
c++ ;
}
return c;
}
int main ( ) {
freopen ( "quarantine.in" , "r" , stdin ) ;
freopen ( "quarantine.out" , "w" , stdout ) ;
srand ( time ( 0 ) ) ;
scanf ( "%d%d%d" , & n, & m, & q) ; char ch= getchar ( ) ;
for ( i= 1 ; i< n; i++ ) for ( j= 0 ; j< m; j++ ) {
while ( ch!= '0' && ch!= '1' ) ch= getchar ( ) ;
for ( k= 0 ; k< m; k++ ) a[ i] [ j] [ k] = ch- '0' , ch= getchar ( ) ;
}
maketree ( 1 , 1 , n- 1 ) ;
while ( q-- ) {
ch= getchar ( ) ; while ( ch!= 'M' && ch!= 'T' ) ch= getchar ( ) ;
if ( ch== 'T' ) {
scanf ( "%d%d%d" , & i, & j, & k) , j-- , k-- ;
a[ i] [ j] [ k] ^ = 1 , change ( 1 , 1 , n- 1 , i) ;
} else {
int l, r; scanf ( "%d%d" , & l, & r) ;
memset ( S. a, 0 , sizeof ( S. a) ) ;
for ( i= 0 ; i< m; i++ ) S. a[ i] [ i] = 1 ;
find ( 1 , 1 , n- 1 , l, r- 1 ) ;
ch= getchar ( ) ; while ( ch!= '0' && ch!= '1' ) ch= getchar ( ) ;
d1[ 0 ] = d2[ 0 ] = 0 ;
for ( i= 0 ; i< m; i++ ) {
if ( ch- '0' ) d1[ ++ d1[ 0 ] ] = i;
ch= getchar ( ) ;
}
ch= getchar ( ) ;
for ( i= 0 ; i< m; i++ ) {
if ( ch- '0' ) d2[ ++ d2[ 0 ] ] = i;
ch= getchar ( ) ;
}
memset ( A, 0 , sizeof ( A) ) ;
for ( i= 1 ; i<= d1[ 0 ] ; i++ ) for ( j= 1 ; j<= d2[ 0 ] ; j++ )
A[ i- 1 ] [ j- 1 ] = S. a[ d1[ i] ] [ d2[ j] ] ;
printf ( "%d\n" , getrk ( d1[ 0 ] , d2[ 0 ] ) ) ;
}
}
}