模拟题【质因数分解、排列组合】桃园之礼

桃园之礼

【题目描述】

小林和亮亮在桃园里一起玩游戏。桃园里的桃树成行成列,刚好构成一个 N × M N×M N×M的矩阵,亮亮在某些桃树下放置了一些小礼物,要求小林把所有树下的礼物全部收集起来。小林从左上角的桃树 ( 1 , 1 ) (1,1) (1,1)出发,走到右下角的桃树 ( N , M ) (N,M) (N,M)。他只能沿着路径向下或者向右走,某些桃树下有礼物,他必须到达所有有礼物的树 下并把礼物收集起来。小林在出发前,想请你帮他计算一下,他有多少种不同的走法。由于答案可能很大,你只需要输出答案模 100000000 ( 1 0 8 ) 100000000(10^8) 100000000108后的值即可。

【输入格式】

第一行三个整数 N , M N,M N,M K K K N , M N,M N,M表示矩阵的大小, K K K表示有礼物的桃树的
棵数。

接下来 k k k行,每行两个整数 X , Y X,Y X,Y,表示一棵有礼物的桃树的坐标 ( X , Y ) (X,Y) (X,Y)

【输出格式】

只有一个整数,表示不同的走法数模 100000000 100000000 100000000后的值。

【样例输入】

5 4 1
2 2

【样例输出】

20

【数据规模】

对于 30 % 30\% 30%的数据, 1 < = N , M < = 30 1<=N,M<=30 1<=N,M<=30

对于 100 % 100\% 100%的数据, 1 < = N , M < = 30000 , 0 < = K < = 10000 , 1 < = X < = N , 1 < = Y < = M 1<=N,M<=30000,0<=K<=10000,1<=X<=N,1<=Y<=M 1<=N,M<=300000<=K<=100001<=X<=N1<=Y<=M


先将起点 ( 1 , 1 ) (1,1) (1,1)与终点 ( n , m ) (n,m) (n,m)存入结构体内,再依据 x x x坐标从大到小将结构体排序,如果 x x x坐标相同则按照 y y y坐标从大到小排序。枚举每一点,如果这个点的 y y y坐标比上一个点的 y y y坐标要小,说明答案为 0 0 0

a a a为当前点横坐标与上一个点横坐标之差, b b b为当前点纵坐标与上一个点的纵坐标之差。一共要走 a + b a+b a+b步,选取其中的 a a a步横向走,因此从上一个点到当前点路线条数为 C a + b a C_{a+b}^a Ca+ba

注意此题的模数不是一个质数,因此还要进行质因数分解。


一开始做这道题的时候并没有注意到模数不是质数,因此没有进行质因数分解,只得了一半的分。


代码如下:

#include<bits/stdc++.h>
#define N 10000+10
#define M 60000+10
#define ll long long
using namespace std;

ll n,m,k;
struct node{
	ll x,y;
}a[N];
 
bool v[M];
ll p[M],f[M],cnt,cur;
ll mod=1e8;
ll ans=1;

void prime(){
	for(ll i=2;i<=n+m;i++){
		if(!v[i])p[++cnt]=i;
		
		for(ll j=1;j<=cnt&&i*p[j]<=n+m;j++){
			v[i*p[j]]=true;
			if(i%p[j]==0)break;
		}
	}
}

ll power(ll a,ll &b){
	ll ans=1;
	
	for(;b;b>>=1){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
	}
	
	return ans;
}

void calc(ll x,ll v){
	for(ll i=1;i<=cnt&&p[i]<=x;i++)
		while(x%p[i]==0)f[i]+=v,x/=p[i];
	cur=cur*x%mod;
}

ll C(ll x,ll y){
	if(y>x)return 0;
	cur=1;
	
	for(ll i=x-y+1;i<=x;i++)calc(i,1);
	for(ll i=1;i<=y;i++)calc(i,-1);
	for(ll i=1;i<=cnt&&p[i]<=x;i++)
		cur=(cur*power(p[i],f[i]))%mod;
		
	return cur;
}

bool cmp(node a,node b){
	if(a.x==b.x)return a.y<b.y;
	return a.x<b.x;
}

int main(){
	scanf("%lld%lld%lld",&n,&m,&k);
	prime();
	
	a[0].x=1,a[0].y=1;
	a[k+1].x=n,a[k+1].y=m;
	
	for(ll i=1;i<=k;i++)
		scanf("%lld%lld",&a[i].x,&a[i].y);
		
	sort(a,a+k+2,cmp);
	
	for(ll i=1;i<=k+1;i++){
		ll x=a[i].x-a[i-1].x,y=a[i].y-a[i-1].y;
		if(x<0||y<0){
			puts("0");
			return 0;
		}
		
		ans=(ans*C(x+y,y))%mod;
	}
	
	printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值