1. 程式人生 > 其它 >【題解】【BZOJ】BZOJ3782 上學路線

【題解】【BZOJ】BZOJ3782 上學路線

BZOJ3782 上學路線

BZOJ3782 上學路線

1 題外話

奇怪的DP增加了

2 sol

直接刪去壞點的難度有點大(暴力\(O(nm)\) ),考慮DP

設\(f[i]\) 表示從\((0,0)\) 走到第\(i\) 個壞點,中途不經過其他壞點的方案數

求某個\(f[x]\) 時,我們列舉它第一個經過的壞點\(i\) ,那麼他的貢獻是\(f[i]\times C^{|x_x-x_i|}_{|x_x-x_i|+|y_x-y_i|}\)

後面組合數的意義為從\(i\) 走到\(x\) 的方案數(不考慮是否經過其他壞點)

那麼\(f[x]=\sum_{i\in T} f[i]\times C^{|x_x-i_x|}_{|x_x-i_x|+|x_y-i_y|}\)

初始化時將\((n,m)\) 放入壞點,將每個壞點排序求出$f$即可

3 code

#include <iostream>
#include <cstdio>
#include <cstring> #include <algorithm> using namespace std; const int N=210; const int M=1000010; inline void read(int &x) { x=0; int f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if (ch=='-') { f=-1; } ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } x*=f; } struct point { long long x; long long y; }; point p[N]; long long n,m,t,P; inline bool comp(const point &x,const point &y) { return x.x==y.x?x.y<y.y:x.x<y.x; } int pri[5]; int con; long long fac[5][M],fac_inv[5][M],inv[5][M]; inline void pre() { long long x=P; for(int i=2;i*i<=x;i++) { while (x%i==0) { pri[++con]=i; x/=i; } } if (x!=1) { pri[++con]=x; } for(int i=1;i<=con;i++) { fac[i][0]=fac[i][1]=fac_inv[i][0]=fac_inv[i][1]=inv[i][0]=inv[i][1]=1; int mod=pri[i]; for(int j=2;j<pri[i];j++) { fac[i][j]=1ll*fac[i][j-1]*j%mod; inv[i][j]=1ll*(mod-mod/j)*inv[i][mod%j]%mod; fac_inv[i][j]=1ll*fac_inv[i][j-1]*inv[i][j]%mod; } } } long long lucas(long long n,long long m,int pid) { if (m>n) { return 0; } if (n<pri[pid]&&m<pri[pid]) { return 1ll*fac[pid][n]*fac_inv[pid][m]%pri[pid]*fac_inv[pid][n-m]%pri[pid]; } return 1ll*lucas(n%pri[pid],m%pri[pid],pid)*lucas(n/pri[pid],m/pri[pid],pid)%pri[pid]; } long long C(long long n,long long m) { if (m>n) { return 0; } long long res=0; for(int i=1;i<=con;i++) { res=(res+1ll*lucas(n,m,i)*(P/pri[i])%P*inv[i][(P/pri[i])%pri[i]]%P)%P; } return res; } long long f[N]; int main() { scanf("%lld%lld%lld%lld",&n,&m,&t,&P); pre(); for(int i=1;i<=t;i++) { scanf("%lld%lld",&p[i].x,&p[i].y); } t++; p[t].x=n,p[t].y=m; sort(p+1,p+t+1,comp); for(int i=1;i<=t;i++) { f[i]=C(0ll+p[i].x+p[i].y,0ll+p[i].x); for(int j=1;j<i;j++) { if (p[j].x<=p[i].x&&p[j].y<=p[i].y) { f[i]=(f[i]-1ll*f[j]*C(p[i].x-p[j].x+p[i].y-p[j].y,p[i].x-p[j].x)%P)%P; } } f[i]=(f[i]+P)%P; } printf("%lld\n",f[t]); return 0; }

4 注意

模數不一定是質數,最後要用CRT合併

Author: tt66ea

Created: 2021-08-12 週四 16:18

Validate