UVA12633 Super Rooks on Chessboard

为了方便接下来的讨论,以左下角作为原点。

这样每一条红色的线上的格点坐标 \((x,y)\) 的和是一定的,可以对每一条红线考虑。

  • 红线上有车

这条红线显然对答案没有贡献

  • 红线上没有车

这样我们只需要考虑横纵的车产生的影响。

\(f_i\) 为第 \(i\) 行是否有车, \(g_i\) 为第 \(i\) 列是否有车。( \(f_i=0\) 表示有, \(f_i=1\) 表示没有, \(g_i\) 同理 )

对于第 \(i\) 条红线上没有被覆盖的格点数,有

\[h_i=\sum_{p+q=i+1}h_pg_q \]

这是一个卷积的形式,使用 fft / ntt 可以 \(\mathcal O(n \log n)\) 求出 \(h\)

最后对于所有没有车的红线的 \(h\) 求和即可。

好像 fft 精度有点爆炸,改成 ntt 就没事了。

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
#define pi acos( -1 )

#define Poly vector< int >
#define len( x ) ( (int)x.size() )

const int MAXN = 4e5 , Mod = 998244353;
int Add( int x , int y ) { x += y; return x >= Mod ? x - Mod : x; }
int Sub( int x , int y ) { x -= y; return x < 0 ? x + Mod : x; }
int Mul( int x , int y ) { return 1ll * x * y % Mod; }
int Quick_pow( int x , int po ) { int Ans = 1; for( ; po ; po >>= 1 , x = Mul( x , x ) ) if( po & 1 ) Ans = Mul( Ans , x ); return Ans; }
int Inv( int x ) { return Quick_pow( x , Mod - 2 ); }
int Div( int x , int y ) { return Mul( x , Inv( y ) ); }

const int G = 3 , iG = 332748118;
int lim , ilim , rev[ MAXN + 5 ];
void ntt( Poly &f , int ty ) {
	for( int i = 0 ; i < lim ; i ++ ) if( i < rev[ i ] ) swap( f[ i ] , f[ rev[ i ] ] );
	for( int len = 2 ; len <= lim ; len <<= 1 ) {
		int w = Quick_pow( ty == 1 ? G : iG , ( Mod - 1 ) / len );
		for( int l = 0 ; l < lim ; l += len ) {
			for( int i = l , wk = 1 ; i < l + len / 2 ; i ++ , wk = Mul( wk , w ) ) {
				int t = Mul( wk , f[ i + len / 2 ] );
				f[ i + len / 2 ] = Sub( f[ i ] , t ); f[ i ] = Add( f[ i ] , t ); 
			}
		}
	}
	
	if( ty == -1 ) for( int i = 0 ; i < lim ; i ++ ) f[ i ] = Mul( f[ i ] , ilim );
}
Poly operator * ( Poly f , Poly g ) {
	int n = len( f ) + len( g ) - 1; for( lim = 1 ; lim < n ; lim <<= 1 ); ilim = Inv( lim );
	for( int i = 0 ; i < lim ; i ++ ) rev[ i ] = ( rev[ i >> 1 ] >> 1 ) | ( i & 1 ? lim >> 1 : 0 );
	f.resize( lim ); g.resize( lim );
	ntt( f , 1 ); ntt( g , 1 );
	for( int i = 0 ; i < lim ; i ++ ) f[ i ] = Mul( f[ i ] , g[ i ] );
	ntt( f , -1 ); f.resize( n );
	return f;
}

int T , n , m , k;
bool rk[ MAXN + 5 ];
Poly f , g , h; 

int main( ) {
	scanf("%d",&T);
	for( int t = 1 ; t <= T ; t ++ ) {
		memset( rk , 0 , sizeof( rk ) );
		
		scanf("%d %d %d",&n,&m,&k);
		f.resize( n + 1 ) , g.resize( m + 1 );
		for( int i = 1 ; i <= n ; i ++ ) f[ i ] = 1;
		for( int i = 1 ; i <= m ; i ++ ) g[ i ] = 1;
		for( int i = 1 , x , y ; i <= k ; i ++ ) {
			scanf("%d %d",&x,&y); x = n - x + 1;
			rk[ x + y ] = 1;
			f[ x ] = 0 , g[ y ] = 0;
		}
		h = f * g;
		
		long long Ans = 0;
		for( int i = 2 ; i <= n + m ; i ++ )
			if( !rk[ i ] ) Ans += h[ i ];
		printf("Case %d: %lld\n", t , Ans );
	}
	return 0;
}
posted @ 2021-03-11 21:26  chihik  阅读(100)  评论(0编辑  收藏  举报