Live2D

Solution -「多校联训」染色

\(\mathcal{Description}\)

  Link.

  给定 \(n\)\(q\) 次询问,每次询问给出 \(x,k\),求第 \(x\) 位为 0 且任意两个 1 的下标之差不小于 \(k\) 的长度为 \(n\) 的 01 序列数量。

  \(n,q\le10^5\)

\(\mathcal{Solution}\)

  “不小与 \(k\)”可能在复杂度中体现为“\(\div k\)”,考虑根号分治。

  对于 \(k\le\sqrt n\),预处理 \(f(k,i)\) 表示不考虑 \(x\) 的限制,长度为 \(i\) 的序列数量。通过全局答案减去 \(x\) 位为 1 的答案可以 \(\mathcal O(1)\) 回答询问;

  而对于 \(k>\sqrt n\)1 的数量 \(\le\sqrt n\),枚举 1 的数量,把 0{k-1}1 看做一整块,可以隔板法求方案,在用总方案减去左右方案之积即可 \(\mathcal O(\sqrt n)\) 回答询问。

  最终,得到复杂度最坏为 \(\mathcal O((n+q)\sqrt n)\) 的算法。

\(\mathcal{Code}\)

/* Clearink */

#include <cmath>
#include <cstdio>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

const int MAXSN = 316, MAXN = 1e5, MOD = 998244353;
int n, q, thres, f[MAXSN + 5][MAXN + 5], fac[MAXN * 2 + 5], ifac[MAXN * 2 + 5];

inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline int mul( const long long a, const int b ) { return int( a * b % MOD ); }
inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline int mpow( int a, int b ) {
	int ret = 1; 
	for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
	return ret;
}

inline void init() {
	fac[0] = 1;
	rep ( i, 1, 2 * n ) fac[i] = mul( i, fac[i - 1] );
	ifac[2 * n] = mpow( fac[2 * n], MOD - 2 );
	per ( i, 2 * n - 1, 0 ) ifac[i] = mul( i + 1, ifac[i + 1] );

	rep ( i, 1, thres ) {
		int* fi = f[i]; *fi = 1;
		rep ( j, 1, n ) fi[j] = add( fi[j - 1], j >= i ? fi[j - i] : 1 );
	}
}

inline int comb( const int n, const int m ) {
	return n < 0 || n < m ? 0 : mul( fac[n], mul( ifac[m], ifac[n - m] ) );
}

int main() {
	freopen( "color.in", "r", stdin );
	freopen( "color.out", "w", stdout );

	scanf( "%d %d", &n, &q ), thres = sqrt( n );

	init();

	for ( int x, k; q--; ) {
		scanf( "%d %d", &x, &k );
		if ( k <= thres ) {
			printf( "%d\n", sub( f[k][n], mul( x >= k ? f[k][x - k] : 1,
				n + 1 >= x + k ? f[k][n - x - k + 1] : 1 ) ) );
		} else {
			int all = 0, lcnt = 0, rcnt = 0;
			rep ( i, 0, ( n + k - 1 ) / k ) {
				all = add( all, comb( n + k - 1 - i * k + i, i ) );
				lcnt = add( lcnt, comb( x - 1 - i * k + i, i ) );
				rcnt = add( rcnt, comb( n - x - i * k + i, i ) );
			}
			printf( "%d\n", sub( all, mul( lcnt, rcnt ) ) );
		}
	}
	return 0;
}

posted @ 2021-06-17 13:06  Rainybunny  阅读(78)  评论(0编辑  收藏  举报