快速幂
写一个大数的快速幂吧 就是数字大于long long的
原理其实很简单就是拆分一步步的拆分
qpow(ll a,ll b){//这个不能少吧
ll ans=1;
while(b){
if(b&1) ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
ll pqow_mod(ll a,ll len){//a^s[len]^
ll ans=1;
while(len){
if(b[len-1]!='0'){
ll res=str[i]-'0';
ans=ans*qpow(a,res)%mod;
}
a=pqow(a,10)%mod;
/*
原理很简单 举个小例子来讲吧
像 a^23^ = a^20^*a^3^;
这些式子首先进行算出 a^3^ 然后
a=pqow(a,10)%mod;
这一步是把 a^20^ 转换成(a^10^)^2^这样就不会出现越界的情况了
*/
}
}
快速幂求逆元(原理是费马小定理 )
(当然模系为素数这样才能正常使用费马小定理)
问题 B: 计数
时间限制: 1 Sec 内存限制: 128 MB
Alice和Bob在平面直角坐标系中下棋。Alice的棋子初始时在(0,0)位置,要走到(a,b)位置;Bob的棋子初始时在(c,0)位置,要走到(a,d)位置。棋子只能沿x轴或y轴正方向移动若干个单位长度,问有多少种移动方案使两颗棋子的移动路径不相交。
输入
输入一行4个正整数,依次为a,b,c,d。
输出
输出总方案数对质数 100000007 取模的结果。
样例输入 Copy
3 2 1 1
样例输出 Copy
6
提示
【样例解释】
A 走(0,0) → (0,2) → (3,2) 时, B 有 3 种走法:
(1,0) → (1,1) → (3,1)
(1,0) → (2,0) → (2,1) → (3,1)
(1,0) → (3,0) → (3,1)
A 走(0,0) → (0,1) → (1,1) → (1,2) → (3,2)时, B 有 2 种走法。
A 走(0,0) → (0,1) → (2,1) → (2,2) → (3,2)时, B 有 1 种走法。
【数据范围】
对于 50%的数据, a + b <= 20。
对于 70%的数据, a + b <= 2e4。
对于 100%的数据, a + b <= 2e5 且 a > c, b > d。
由题意可以得到,他们可以组成这样的矩形
图形大致是这样的:A0为(0,0)B0为(c,0)A1为(a,b)B1为(a,d);
我们可以得出C(a,a+b);为从A0到A1的方案数C(d,a-c+d)为B0到B1的方案数 他两个相乘得到的就是总的方案数)当然包括重复的
重复的是矩形3*矩形4
证明过程:首先,如果原点目标改为(a,d),那么它的路径上的y一定不会高于d;
从(c,0)点到(a,b)的y一定会竖跨整个0 - b的y坐标,即每个点的y坐标能覆盖0-b;
而从原点开始的路径又会覆盖整个0-a的x坐标,因此他们一定相交。
又因为c路径的x不小于c,a路径的y不大于d,因此他们的交点一定出现在(c,0)−(a,d) (c,0)-(a,d)(c,0)−(a,d)的矩阵中。
同时通过上面的路径交换我们可以发现,这两个相交的路径可以通过变换变成符题目目标要求的道路。
而所有相交的又可以反推出是更改目标的路径,因此 C(a,a+d)*C(b,a-c+b)为所有x相交的方案数
然后我们拿就是所有合法的方案数
C(a,a+b)*C(d,a-c+d)-C(a,a+d)*C(b,a-c+b);
写这道题我学会的主要还是数论上的知识:关于逆元的知识,快速幂求逆元,线性递推求逆元
orz
#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+1010;
#define
const int mod=1e8+7;
const int MOD=10007;
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
ll n,t,m,sum,maxx,ans;
ll a,b,c,d;
//ll a[maxn],b[maxn],l[maxn],r[maxn];
ll f[maxn],dis[maxn];
char str[500][500],s[maxn];
ll pqow(ll a,ll b){//快速幂求逆元
ll res=1;
while(b){
if(b&1) res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
ll C(ll n,ll m)//组合数
{
return (f[m]%mod*dis[n]%mod*dis[m-n]%mod)%mod;
}
int main(){
cin>>a>>b>>c>>d;
f[0]=1;
for(int i=1;i<maxn;i++) f[i]=(f[i-1]%mod*i%mod)%mod;// 阶乘最大逆元
dis[maxn-1]=pqow(f[maxn-1],mod-2);//费马小定理求逆元
for(int i=maxn-1;i>0;i--)
dis[i-1]= dis[i]*i%mod; //阶乘逆元
ans=(C(a,a+b) * C(a-c,a-c+d))%mod - (C(a,a+d)*C(a-c,a-c+b))%mod;//前面是总的 后面是重复的
if(ans<0) ans+=mod;//防止出现负数
cout<<ans<<endl;
return 0;
}