1. 程式人生 > >[BZOJ 4403] 序列統計

[BZOJ 4403] 序列統計

type bits sign pri else tchar 階乘 blank const

[題目鏈接]

https://www.lydsy.com/JudgeOnline/problem.php?id=4403

[算法]

令r - l + 1 = m

若序列的長度為i , 那麽每個數的出現次數x1 , x2 , x3 , .. xm滿足 :

x1 + x2 + x3 + .. + xm = n

插板法 , (x1 , x2 , ... xm)共有C(i + m - 1 , m - 1)種組合

那麽問題就轉化為了求 : C(m , m - 1) + C(m + 1 , m - 1) + ... + C(n + m - 1 , m - 1)

在前面添一項C(m , m)

原式 = C(m , m) + C(m , m - 1) + C(m + 1 , m - 1) + ... + C(n + m - 1 , m - 1)

一個經典的遞推式 : C(n + 1 , m) = C(n , m - 1) + C(n , m)

將原式相鄰兩項依次合並 , 原式 = C(n + m , m) - 1

預處理階乘逆元 , 盧卡斯定理求解模意義下的值 , 即可

時間復雜度 : O(P) (P = 10 ^ 6 + 3)

[代碼]

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e9 + 10;
const int P = 1e6 + 3; 
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;

int fac[P + 1] , inv[P + 1];

template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); }
template <typename T> inline void
chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == -) f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - 0; x *= f; } inline int exp_mod(int a , int n) { int b = a , res = 1; while (n > 0) { if (n & 1) res = 1ll * res * b % P; b = 1ll * b * b % P; n >>= 1; } return res; } inline void init() { fac[0] = 1; for (int i = 1; i <= P; i++) fac[i] = 1ll * fac[i - 1] * i % P; inv[P - 1] = exp_mod(fac[P - 1] , P - 2); for (int i = P - 2; i >= 1; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % P; } inline int C(int x , int y) { if (x < y) return 0; else return 1ll * fac[x] * inv[y] % P * 1ll * inv[x - y] % P; } inline int lucas(int n , int m) { if (n < m) return 0; if (n == m || !m) return 1; if (n < P && m < P) return C(n , m); else return 1ll * lucas(n / P , m / P) * C(n % P , m % P) % P; } int main() { int T; init(); read(T); while (T--) { int n , l , r; read(n); read(l); read(r); printf("%d\n" , (lucas(n + r - l + 1 , r - l + 1) - 1 + P) % P); } return 0; }

[BZOJ 4403] 序列統計