Solution -「多校聯訓」染色
阿新 • • 發佈:2021-06-17
\(\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
而對於 \(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; }