BZOJ 3782 上学路线 ——动态规划 Lucas定理 中国剩余定理
2024-09-02 13:21:00
我们枚举第一个经过的坏点,然后DP即可。
状态转移方程不是难点,难点在于组合数的处理。
将狼踩尽的博客中有很详细的证明过程,但是我只记住了结论
$n=a_1 * p^k+a_2*p^k-1...$
$m=b_1 * p^k+b_2*p^k-1...$
$C(_{m}^{n})=C(_{b_1}^{a_1})*...$
大概的意思就是转化成$p$进制下的每一位做组合数,那么我们就可以预处理阶乘以及它的逆元进行计算。
所以说Lucas只能跑过$10^5$当质数很大的时候就放弃。
如果不是质数,那么可以分解质因数,每一个因数做一次Lucas,然后用CRT合并。
以前留下的大坑终于补完了,EXGCD和CRT终于明白了(真是弱(。・・)ノ)
突然发现namespace写起来挺好用的,以后挂链就用它了
注意 1.n<m时候需要判定,因为%意义下会有小的情况产生。
2.随时取模
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define ll long long
#define mp make_pair namespace Subtask1{
const int p=1000003;
ll fac[p],inv[p];
void Shaker()
{
int i;
for (fac[0]=1,i=1;i<p;++i)
fac[i]=fac[i-1]*i%p;
for (inv[1]=1,i=2;i<p;++i)
inv[i]=(p-p/i)*inv[p%i]%p;
for (inv[0]=1,i=1;i<p;++i)
(inv[i]*=inv[i-1])%=p;
}
ll C(ll n,ll m)
{
if (n<m) return 0;
if (n<p&&m<p) return fac[n]*inv[m]*inv[n-m]%p;
return C(n%p,m%p)*C(n/p,m/p)%p;
}
} namespace Subtask2{
const int p=1019663265;
int pri[]={3,5,6793,10007};
ll fac[4][10007],inv[4][10007],k1=339887755,k2=407865306,k3=673070820,k4=618502650;
void Shaker()
{
int i,j;
for (j=0;j<4;++j)
{
int np=pri[j];
for (fac[j][0]=1,i=1;i<np;++i)
fac[j][i]=fac[j][i-1]*i%np;
for (inv[j][1]=1,i=2;i<np;++i)
inv[j][i]=(np-np/i)*inv[j][np%i]%np;
for (inv[j][0]=1,i=1;i<np;++i)
(inv[j][i]*=inv[j][i-1])%=np;
}
}
ll C(ll n,ll m,int j)
{
int np=pri[j];
if (n<m) return 0;
if (n<np&&m<np) return fac[j][n]*inv[j][m]*inv[j][n-m]%np;
return C(n%np,m%np,j)*C(n/np,m/np,j)%np;
}
ll C(ll n,ll m)
{
ll r1=C(n,m,0),r2=C(n,m,1),r3=C(n,m,2),r4=C(n,m,3);
return (r1*k1+r2*k2+r3*k3+r4*k4)%p;
}
} struct Point{
ll x,y;
void read(){scanf("%lld%lld",&x,&y);}
}points[220]; bool cmp(Point a,Point b)
{return a.x==b.x?a.y<b.y:a.x<b.x;} ll ksm(ll a,ll b,ll p)
{
ll ret=1;
for (;b;b>>=1,(a*=a)%=p)
if (b&1) (ret*=a)%=p;
return ret;
} ll f[220],t,n,m; void solve1()
{
using namespace Subtask1;
Shaker();
F(i,1,t)
{
f[i]=C(points[i].x+points[i].y,points[i].x);
F(j,1,i-1) if (points[j].y<=points[i].y)
{
(f[i]-=f[j]*C(points[i].x-points[j].x+points[i].y-points[j].y,points[i].x-points[j].x)%p);
f[i]+=p; f[t]%=p;
}
}
} void solve2()
{
using namespace Subtask2;
Shaker();
F(i,1,t)
{
f[i]=C(points[i].x+points[i].y,points[i].x);
F(j,1,i-1) if (points[j].y<=points[i].y)
{
(f[i]-=f[j]*C(points[i].x-points[j].x+points[i].y-points[j].y,points[i].x-points[j].x)%p);
f[i]%=p;f[i]+=p; f[t]%=p;
}
}
} void Finout()
{
freopen("in.txt","r",stdin);
freopen("wa.txt","w",stdout);
} int main()
{
ll p;
scanf("%lld%lld%lld%lld",&n,&m,&t,&p);
F(i,1,t) points[i].read();++t;points[t].x=n;points[t].y=m;
sort(points+1,points+t+1,cmp);
if (p==1000003) solve1(); else solve2();
cout<<f[t]<<endl;
}
最新文章
- Android Studio模拟器的问题及解决办法
- SQL-PIVOT 数据透视 行列转换
- ASP.NET中的事件处理
- oracle 本地使用命令导入数据到远程主机
- jQuery基础选择器
- 【转】Ext JS xtype
- android的生命周期
- 对mysql进行分表
- Ubuntu安装字体的方法
- Linq 内联左联等
- sealed的作用
- 201521123104 《Java程序设计》第8周学习总结
- cesium编程入门(六)添加 3D Tiles,并调整位置,贴地
- 工频相位无线同步模块PSYN5000系列在高压设备状态检测和局部放电故障定位的应用方案
- SAP MM ME1M报表结果不科学?
- Listener(2)—案例
- P3181 [HAOI2016]找相同字符
- VD: error VERR_FILE_NOT_FOUND
- 译:2. RabbitMQ Java Client 之 Work Queues (工作队列)
- Linux环境下NodeJS和MongoDB的安装配置
热门文章
- Android中的ListView属性使用总结
- PHP 根据两点的经纬度计算距离
- UISearchBar clearButton
- 你是猴子请来的逗比么!IT跳槽大事件
- (转)MyBatis框架的学习(七)——MyBatis逆向工程自动生成代码
- python学习(day1)
- Luogu P4463 [国家集训队] calc
- pb2.text_format.Merge(f.read(), self.solver_param) AttributeError: &#39;module&#39; object has no attribute &#39;text_format&#39;
- Fortran学习记录3(选择语句)
- Luogu P2664 树上游戏 dfs+树上统计