1. 程式人生 > >[BZOJ4737][清華集訓2016]組合數問題(數位 DP )

[BZOJ4737][清華集訓2016]組合數問題(數位 DP )

Address

BZOJ4737
UOJ#275

Solution

根據 Lucas 定理,當 k k 是質數時:
( n m

) i ( n i
m i
)
(   m o
d
  k ) \binom nm\equiv\prod_{i}\binom{n_i}{m_i}(\bmod k)
其中 n i n_i m i m_i 分別表示 n n m m k k 進位制意義下第 i i 位的值。
所以問題轉化成有多少對 0 i n 0\le i\le n 0 j min ( i , m ) 0\le j\le\min(i,m) 滿足 k k 進位制意義下存在一位 x x 滿足 i x < j x i_x<j_x
相當於用 ( n + 1 ) × ( n + 2 ) 2 max ( 0 , n m ) × ( max ( 0 , n m ) + 1 ) 2 \frac{(n+1)\times(n+2)}2-\frac{\max(0,n-m)\times(\max(0,n-m)+1)}2
減去對於每一位 x x 都滿足 i x j x i_x\ge j_x 的方案數。
同時如果 i < j i<j 則一定存在一位滿足 i x < j x i_x<j_x ,所以只需要把 min ( i , m ) \min(i,m) 改成 m m 之後,就可以進行數位 DP 了。
f [ i ] [ 0 / 1 ] [ 0 / 1 ] f[i][0/1][0/1] 表示到第 i i 位,第二維表示是否超過 n n ,第三維表示是否超過 m m
大力轉移。複雜度 O ( T k 2 log max ( n , m ) ) O(Tk^2\log\max(n,m))

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)

template <class T>
T Max(T a, T b) {return a > b ? a : b;}

typedef long long ll;

const int N = 105, ZZQ = 1e9 + 7;

inline void add(int &x, int y)
{
	x += y;
	if (x >= ZZQ) x -= ZZQ;
}

ll n, m;
int T, K, na, nb, a[N], b[N], f[N][2][2], ans;

void work()
{
	int i, j, k;
	na = nb = 0;
	memset(f, 0, sizeof(f));
	scanf("%lld%lld", &n, &m);
	ll tn = n, tm = m;
	memset(a, 0, sizeof(a));
	memset(b, 0, sizeof(b));
	while (n) a[++na] = n % K, n /= K;
	while (m) b[++nb] = m % K, m /= K;
	For (i, 0, K - 1) For (j, 0, i)
		f[1][i > a[1]][j > b[1]]++;
	For (i, 2, Max(na, nb)) For (j, 0, K - 1) For (k, 0, j)
		if (j < a[i] && k < b[i])
		{
			add(f[i][0][0], (f[i - 1][0][0] + f[i - 1][0][1]) % ZZQ);
			add(f[i][0][0], (f[i - 1][1][0] + f[i - 1][1][1]) % ZZQ);
		}
		else if (j < a[i] && k > b[i])
		{
			add(f[i][0][1], (f[i - 1][0][0] + f[i - 1][0][1]) % ZZQ);
			add(f[i][0][1], (f[i - 1][1][0] + f[i - 1][1][1]) % ZZQ);
		}
		else if (j > a[i] && k < b[i])
		{
			add(f[i][1][0], (f[i - 1][0][0] + f[i - 1][0][1]) % ZZQ);
			add(f[i][1][0], (f[i - 1][1][0] + f[i - 1][1][1]) % ZZQ);
		}
		else if (j > a[i] && k > b[i])
		{
			add(f[i][1][1], (f[i - 1][0][0] + f[i - 1][0][1]) % ZZQ);
			add(f[i][1][1], (f[i - 1][1][0] + f[i - 1][1][1]) % ZZQ);
		}
		else if (j == a[i] && k == b[i])
		{
			add(f[i][0][0], f[i - 1][0][0]); add(f[i][0][1], f[i - 1][0][1]);
			add(f[i][1][0], f[i - 1][1][0]); add(f[i][1][1], f[i - 1][1][1]);
		}
		else if (j < a[i] && k == b[i])
		{
			add(f[i][0][0], (f[i - 1][0][0] + f[i - 1][1][0]) % ZZQ);
			add(f[i][0][1