matrix
Posted $mathcal{Yubai}$
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了matrix相关的知识,希望对你有一定的参考价值。
\\(link\\)
不得不说是一道神题
先说暴力状压作法,枚举他转移到的地方,记录没列选没选过就行了。
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <iostream>
#define int long
#define R register int
#define printf Ruusupuu = printf
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
const int N = 18 ;
int Ruusupuu ;
inline int lb( int x ){ return x & -x ; }
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 ^ \'0\' ) , ch = getchar() ;
return fg ? -w : w ;
}
int n , m , k , l [N] , r [N] , ans , f [N][1 << N] , lg [40] ;
void sc(){
n = read() , m = read() ;
for( R i = 1 ; i <= n ; i ++ ) l [i] = read() , r [i] = read() ;
}
inline void debug( int x ){ cout << bitset< 10 > ( x ) << endl ; }
void work(){
for( R i = 0 ; i < 36 ; i ++ ) lg[( 1ll << i ) % 37] = i ;
f [0][0] = 1 ;
for( R i = 0 ; i <= n ; i ++ ){
for( R j = 0 ; j < 1 << m ; j ++ ){
if( !f [i][j] ) continue ;
for( R k = 1 ; k <= l [i + 1] ; k ++ ){
for( R s = r [i + 1] ; s <= m ; s ++ ){
if( ( ( j >> ( k - 1 ) ) & 1 ) || ( ( j >> ( s - 1 ) ) & 1 ) ) continue ;
int t = j | ( 1 << ( k - 1 ) ) ;
t |= ( 1 << ( s - 1 ) ) ;
f [i + 1][t] += f [i][j] ;
// printf( "%ld %ld\\n" , i , f [i][j] ) ; debug( j ) ; debug( t ) ;
}
}
}
if( i == n ) for( R j = 0 ; j < 1 << m ; j ++ ) ans += f [i][j] ;
} printf( "%ld\\n" , ans ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
正解按列\\(dp\\),他的状态定义特别神。
\\(f[i][j]\\)表示在第\\(i\\)列,在右侧区间放了\\(j\\)个1的方案数。
有一个事情,就是你想在右侧区间放1,那么首先右侧区间的左端点要在\\(i\\)左边
然后开始转移
如果在第\\(i\\)列不在右区间放1
如果放
其中\\(rr[i]\\)代表在第\\(i\\)列,有多少右区间的的左端点在\\(i\\)左边,这个很好实现,一个前缀和就行了。
在第\\(i\\)列上放1,一共有\\(rr[i]-j+1\\)行支持这个操作,因为已经放了\\(j-1\\)行了。
这个状态定义最神的地方在于他不定义左区间相关内容,因为左右区间有可能相互干扰,所以他干脆就不定义。
每当到达一列,如果这一列是左区间的右端点,再考虑在这个做区间放1
这个需要我们对左端点前缀和,设为\\(ll[i]\\)表示在第\\(i\\)点和之前有多少个左端点。
那么,到达第\\(i\\)列,有\\(ll[i]-ll[i-1]\\)个区间的左端点在第\\(i\\)列上,我们要将这些左区间填上1(不然之后就没机会填1了)
我现在需要\\(ll[i]-ll[i-1]\\)列来放下这些1,我还有\\(i-j-ll[i-1]\\)列,因为各个之间的顺序不同会导致最终方案不同排列一下就行了。
\\(f[i][j]*=A_{i-j-ll[i-1]}^{ll[i]-ll[i-1]}\\)
回想一下这道题,他通过这个状态定义,成功化解了左右区间相互干扰的问题。
通过只统计当前列左边的右区间并记录,只在第\\(i\\)列上看放不放1,消除了在左区间放对他的干扰,同时给左区间提供了信息。
通过只到左区间的右端点在放1,保证了可以利用上右端点占用了多少列的信息,同时不用把它装到状态定义中,保证了时空复杂度。
不得不说真的神。
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long
#define R register int
#define printf Ruusupuu = printf
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
const int N = 3e3 + 10 ;
const int M = 998244353 ;
int Ruusupuu ;
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 ^ \'0\' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline int T( int a , int b ){ return 1ll * a * b % M ; }
inline int J( int a , int b ){ return a + b >= M ? a + b - M : a + b ; }
int n , m , r [N] , l [N] , f [N][N] , ll [N] , rr [N] , js [N] , inv [N] ;
void sc(){
n = read() , m = read() ;
for( R i = 1 ; i <= n ; i ++ ) l [i] = read() , ll [l [i]] ++ , r [i] = read() , rr [r [i]] ++ ;//, printf( "%ld %ld\\n" , i , n ) ;
for( R i = 1 ; i <= m ; i ++ ) ll [i] += ll [i - 1] , rr [i] += rr [i - 1] ;//, printf( "LR%ld %ld\\n" , ll [i] , rr [i] ) ;
}
inline int A( int n , int m ){
return T( js [n] , inv [n - m] ) ;
}
void work(){
f [0][0] = 1 ; js [0] = js [1] = inv [0] = inv [1] = 1 ;
for( R i = 2 ; i < N ; i ++ ) js [i] = T ( js [i - 1] , i ) , inv [i] = T ( ( M - M / i ) , inv [M % i] ) ;//, printf( "%ld %ld\\n" , js [i] , inv [i] ) ;
for( R i = 1 ; i < N ; i ++ ) inv [i] = T ( inv [i - 1] , inv [i] ) ;//, printf( "%ld\\n" , inv [i] ) ;
for( R i = 1 ; i <= m ; i ++ ){ //dp每一列
for( R j = 0 ; j <= min ( i , n ) ; j ++ ){
if( !j ) f [i][j] = f [i - 1][j] ;
else f [i][j] = J ( f [i - 1][j] , T ( f [i - 1][j - 1] , max( 0l , ( rr [i] - j + 1 ) ) ) ) ;
f [i][j] = T( f [i][j] , A( i - j - ll [i - 1] , ll [i] - ll [i - 1] ) ) ;
// printf( "%ld %ld %ld\\n" , i , j , f [i][j] ) ;
}
} printf( "%ld\\n" , f [m][n] ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
以上是关于matrix的主要内容,如果未能解决你的问题,请参考以下文章
leetcode_1292. Maximum Side Length of a Square with Sum Less than or Equal to Threshold_[二维前缀和](代码片段