4338: BJOI2015 糖果

4338: BJOI2015 糖果

Time Limit: 2 Sec   Memory Limit: 256 MB
Submit: 141   Solved: 66
[ Submit][ Status][ Discuss]

Description

Alice 正在教她的弟弟 Bob 学数学。 
每天,Alice 画一个N行M 列的表格,要求 Bob在格子里填数。 
Bob已经学会了自然数1到K的写法。因此他在每个格子里填1 ~ K之间的整数。 
Alice 告诉 Bob,如果 Bob 填写完表格的 N*M 个数以后,每行的数从第 1 列到第 M
列单调不减,并且任意两行至少有一列的数不同,而且以前 Bob 没有填写过相同的表格,
那么Alice 就给Bob吃一颗糖果。 
Bob想知道,如果每天填写一遍表格,最多能吃到多少颗糖果。 
答案模P输出。 

Input

第一行,四个整数依次是N, M, K, P。 

Output

输出一行,一个整数,表示答案模P 后的结果。 

Sample Input

【样例输入1】
1 3 3 10
【样例输入2】
2 2 2 10

Sample Output

【样例输出1】
0
【样例输出2】
6

HINT

【样例解释】 

样例1。表格只有一行。每个格子可以填写1 ~ 3。有10种填写方法,依次为1 1 1,

1 1 2,1 1 3,1 2 2,1 2 3,1 3 3,2 2 2,2 2 3,2 3 3,3 3 3。   

样例2。表格有两行。有6 种填写方法,依次为  1 1/1 2, 1 1/2 2, 1 2/1 1, 1 2/2 

2, 2 2/1 1, 2 2/1 2。 

 

【数据规模与约定】 

100% 的数据中,1 ≤ N, M ≤ 10^5,1 ≤ P, K ≤ 2*10^9. 

Source

[ Submit][ Status][ Discuss]




#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<bitset>
using namespace std;

const int maxn = 1E5 + 10;
typedef long long LL;

LL N,M,K,P,r1,a1,fac[maxn],inv[maxn];
int tot,pri[maxn];
bool not_pri[maxn];

void Extend_Gcd(LL a,LL &x,LL b,LL &y)
{
	if (!b) {x = 1; y = 0; return;}
	Extend_Gcd(b,y,a%b,x); y -= x*(a/b);
}

LL C(int n,int m,LL g)
{
	if (m > n) return 0;
	LL Inv = inv[m]*inv[n-m]%g;
	return fac[n]*Inv%g;
}

LL Lucas(LL n,LL m,LL g)
{
	if (!m) return 1;
	return Lucas(n/g,m/g,g)*C(n%g,m%g,g)%g;
}

LL ksm(LL x,int y,LL g)
{
	LL ret = 1;
	for (; y; y >>= 1)
	{
		if (y&1) ret = ret*x%g;
		x = x*x%g;
	}
	return ret;
}

void Work(LL a2)
{
	LL sum = 1,r2 = 1;
	if (a2 < maxn)
	{
		fac[0] = 1;
		for (LL i = 1; i < a2; i++) fac[i] = fac[i-1]*i%a2;
		inv[a2-1] = ksm(fac[a2-1],a2-2,a2);
		for (LL i = a2-2; i >= 0; i--) inv[i] = inv[i+1]*(i+1)%a2;
		sum = Lucas(K + M - 1,M,a2);
	}
	else
	{
		for (LL i = 1; i <= M; i++)
		{
			sum = sum*(K + M - i)%a2; LL x,y; 
			Extend_Gcd(i,x,a2,y);
			sum = sum*x%a2;
		}
	}
	for (int i = 0; i < N; i++) r2 = r2*(sum-i)%a2;
	r2 = (r2 + a2) % a2;
	if (!a1) {r1 = r2; a1 = a2; return;}
	LL x,y,k1,g; Extend_Gcd(a1,x,a2,y);
	k1 = (r2 - r1)*x%a2; k1 = (k1 + a2) % a2;
	g = a1*a2; r1 = (k1*a1%g + r1) % g; a1 = g;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	for (int i = 2; i < maxn; i++)
	{
		if (!not_pri[i]) pri[++tot] = i;
		for (int j = 1; j <= tot; j++)
		{
			int Nex = i*pri[j];
			if (Nex >= maxn) break;
			not_pri[Nex] = 1;
			if (i % pri[j] == 0) break;
		}
	}
	
	cin >> N >> M >> K >> P; int _P = P;
	for (int i = 1; i <= tot; i++)
	{
		if (_P % pri[i] != 0) continue;
		while (_P % pri[i] == 0) _P /= pri[i];
		Work(pri[i]);
	}
	if (_P > 1) Work(_P);
	cout << r1;
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值