JZOJ5612. 【NOI2018模拟3.29】第3题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/XLno_name/article/details/83105246

题意:

数据范围:

Analysis:

20分很显然的设 f i , j f_{i,j} DP。
50分,观察这个三个式子。类似于每次走(x+1,y),(x,y+1),根据一个步数可以确定另一个,然后组合数算,是否能够类似的做。假设我们每个走了 x , y , z x,y,z 次。就有方程:
x + y + 2 z = n x+y+2z=n x y = m x-y=m
解得: x = y + m , z = n m 2 y x=y+m,z=\frac{n-m}{2}-y
也就是说对于一个确定的 y y ,其它也随之确定。显然可以用组合数算。
设: A = n m 2 , B = n + m 2 A=\frac{n-m}{2},B=\frac{n+m}{2}
有: i = 0 A C i + B i C B i + m \sum_{i=0}^{A}C_{i+B}^{i}C_{B}^{i+m}
100分:快速计算组合数,又有模数。套路的用 L u c a s Lucas 定理,在 m o mo 进制下DP。因为有 i + m i+m ,要多设一维 0 / 1 0/1 表示是否进位。
但还有一种更简单的做法:发现第三种走法等于走一次第一种,走一次第二种。
不妨假设只有第一,二种,合并出第三种。枚举多少个第一二种配对第三种,并且其可以变成第三种也可以不变成第三种,则有式子:
i = 0 A 2 i C A i C B i \sum_{i=0}^{A}2^iC_{A}^iC_{B}^i
这个直接逐位枚举 m o mo 进制下每一位的数算就好了。

Code:

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 2e5 + 5;
const int mo = 100003;
typedef long long ll;
int inv[N],fac[N],fac_[N];
ll n,m;
inline int pow(int x,int p)
{
	int ret = 1;
	for (; p ; p >>= 1,x = (ll)x * x % mo)
	if (p & 1) ret = (ll)ret * x % mo;
	return ret;
}
inline int C(ll n,ll m)
{
	if (n < m) return 0;
	if (n >= mo) return (ll)C(n / mo,m / mo) * C(n % mo,m % mo) % mo;
	return (ll)fac[n] * fac_[m] % mo * fac_[n - m] % mo;
}
int main()
{
	freopen("move.in","r",stdin);
	freopen("move.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	if (n < m || (n + m) % 2) { puts("0"); return 0; }
	if (n == m) { puts("1"); return 0; }
	ll A = (n - m) / 2,B = (n + m) / 2; fac[0] = fac_[0] = inv[1] = 1;
	for (int i = 1 ; i < mo ; ++i)
	{
		if (i > 1) inv[i] = (ll)(mo - mo / i) * inv[mo % i] % mo;
		fac[i] = (ll)fac[i - 1] * i % mo;
		fac_[i] = (ll)fac_[i - 1] * inv[i] % mo;
	}
	int ans = 1;
	while (A > 0)
	{
		int l = A % mo,r = B % mo,now = 0;
		A /= mo,B /= mo;
		for (int i = 0 ; i <= min(l,r) ; ++i) now = (now + (ll)C(l,i) * C(r,i) % mo * pow(2,i) % mo) % mo;
		ans = (ll)ans * now % mo;
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/XLno_name/article/details/83105246
今日推荐