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

\\[f[i][j]+=f[i-1][j] \\]

如果放

\\[f[i][j]+=f[i-1][j-1]*(rr[i]-j+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_[二维前缀和](代码片段

R 代码:model.matrix.default(mt, mf, contrasts) 中的错误:变量 1 没有级别

《Python机器学习及实践》----无监督学习之特征降维

《Python机器学习及实践》----无监督学习之特征降维

Gym 100917M Matrix, The

Android Matrix