7.22T2
听学长讲了一波数论杂题,有点蒙,刚打完T3的模拟,决定早定写完T2题解,去磕T3的正解,不过事实上我T2没有A掉,只拿了WA60,你问我为什么没A敢写题解?因为我看了好久CRT,没看懂,所以决定有时间再打,先写一下T2的思路
30%
关于$30%t<=100$,空间和时间上$dp$都是可以承受的,用$dp[i][j][k]$代表在i时刻到达$(j,k)$这个点,其实很好想,每个点都有它上下左右的4个点转移而来,这个勤取模就可以了,不会被乱七八糟的模数为质为合干掉,不过我开始一直在$WA10$,还以为思路上有毛病,结果问过之后才知道,是可以走出$n*m$的格子的,这样的话边界就不能是$(n,m)$了,数组需要开大的同时,由于下标不能越界出负值,所以需要把所有的点向上,向右平移$t$个单位
60%
考试的时候我其实推出来了一部分式子,当时我以为都是那种很显然的废话,结果。。。。。。他是正解的来源,一开始看了一些题解,都没看懂,最后被WD大神解救了
先来一个导入,前几天学长讲线性基的时候WD有和secret聊过这个式子,且我觉得我听懂了,然而,我想不到,给你n种物品共k件,每种都分别有a1,a2,a3,a4,…,an件,求这k件物品本质不同的选择方案,很显然k个全排列就是$k!$但是内部那些一样的物品都被你当作不一样来用了,那就算多了,所以最后的结果是$\frac{k!}{a_1!*a_2!*a_3!*...*a_n!}$还有一个限制条件是$\sum\limits_{i=1}^{i<=n}a_i=k$
那回到这道题,我们设向左为$l$,向右为$r$,向上为$u$,向下为$d$,那么就会有
$r-l=n$ $u-d=m$ $l+r+u+d=t$
即$l+(n+l)+d+(m+d)=t$ 移项得$2(l+d)=t-m-n$
证到这我们可以发现,如果$t-m-n$为奇数,那方案数就直接是0(说一个改题时候的小插曲,因为题目里说他可以在t时刻之前到达(n,m),我就以为他到了之后就不走了,结果就算你到达过,你也必须保证t时刻一定在(n,m),是我zz了)
转化一下问题,现在有4种走路选择共$k$步,第一种$l$步,第二种$r$步,第三种$d$步,第四种$u$步,这样的话就成功的转化成了导入里的那道题
如此一来我们就可以枚举$u$或$l$中的任意一个,用$Lucas$或者阶乘逆元直接算都没问题,可是这两种方法都只能解决模数为素数的时候,所以。。。。。
100%
合并$CRT$
A题后的补充
刚打完$CRT$,在让人抓狂的调代码中干掉了这道题,觉得有些东西不是一句合并$CRT$就可以解决的,关于$CRT$的话,记不住板子自己推一推,然而我死在了$Lucas$上
我在打60分代码的时候没有想到逆元可能全被干成0的情况,就是当$t<mod$的时候,因为我快速幂求的逆元所以会有这种情况,不知道为啥6个测试点都没卡我,然后就很抓狂了,我要把一个全是阶乘的式子变成C,原谅我没想出来,最后itawbm小姐姐解救了我,那个$t!$比上一堆东西,最后可以变成三个组合数相乘,$C_{2*a+n}^a*C_{2*b+m}^b*C_t^{2*a+n}$,我抄到算草纸上的时候抄错公式了,不停的WA0,结果还是因为zz,一开始$Lucas$也写错了,还调了半天,难受
T3正解还没看懂,估计明天又要考试了,也不知道能不能把题改完
1 //可以走出n*m的网格,dp转移的话最远可以走到(-t,0),(t,0),(0,-t),(0,t) 2 //这样的话数组最大需要dp[2*t+1][2*t+1],同时需要解决下标为负的问题 3 //若解决下标为负,则应用(t,t)代替(0,0)作为起始点,才能保证数组不越界 4 #include<iostream> 5 #include<cstdio> 6 #include<cmath> 7 #define ll long long 8 #define maxn 110 9 using namespace std; 10 int t,n,m; 11 ll mod; 12 ll dp[maxn*2][maxn*2][maxn*2]; 13 int main() 14 { 15 scanf("%d%lld%d%d",&t,&mod,&n,&m); 16 n=abs(n); m=abs(m); 17 dp[0][t][t]=1; 18 for(int i=1;i<=t;++i) 19 for(int j=t-i;j<=t+i;++j) 20 for(int k=t-i;k<=t+i;++k) 21 { 22 if(j-1>=0) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-1][k])%mod; 23 if(k-1>=0) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k-1])%mod; 24 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j+1][k])%mod; 25 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k+1])%mod; 26 } 27 printf("%lld\n",dp[t][n+t][m+t]%mod); 28 return 0; 29 }
1 //可以走出n*m的网格,dp转移的话最远可以走到(-t,0),(t,0),(0,-t),(0,t) 2 //这样的话数组最大需要dp[2*t+1][2*t+1],同时需要解决下标为负的问题 3 //若解决下标为负,则应用(t,t)代替(0,0)作为起始点,才能保证数组不越界 4 #include<iostream> 5 #include<cstdio> 6 #include<cmath> 7 #define ll long long 8 #define maxn 110 9 #define maxx 100100 10 using namespace std; 11 int t,n,m; 12 ll mod,ans; 13 ll dp[maxn*2][maxn*2][maxn*2]; 14 ll jc[maxx],ny[maxx]; 15 ll ksm(ll a,ll b) 16 { 17 ll ans=1; a=a%mod; 18 while(b) 19 { 20 if(b&1) ans=(ans*a)%mod; 21 b=b>>1; a=(a*a)%mod; 22 } 23 return ans; 24 } 25 int main() 26 { 27 scanf("%d%lld%d%d",&t,&mod,&n,&m); 28 n=abs(n); m=abs(m); 29 if(((t-n-m)&1)!=0) {printf("0\n"); return 0;} 30 if(t<=100) 31 { 32 dp[0][t][t]=1; 33 for(int i=1;i<=t;++i) 34 for(int j=t-i;j<=t+i;++j) 35 for(int k=t-i;k<=t+i;++k) 36 { 37 if(j-1>=0) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-1][k])%mod; 38 if(k-1>=0) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k-1])%mod; 39 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j+1][k])%mod; 40 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k+1])%mod; 41 } 42 printf("%lld\n",dp[t][n+t][m+t]%mod); 43 } 44 else 45 { 46 jc[0]=1; 47 for(int i=1;i<=t+1;++i) jc[i]=(jc[i-1]*i)%mod; 48 ny[t+1]=ksm(jc[t+1],mod-2); 49 for(int i=t+1;i>=1;--i) ny[i-1]=(ny[i]*i)%mod; 50 for(int a=0;a<=(t-m-n)/2;++a) 51 { 52 int b=(t-n-m)/2-a; 53 ll ls1=(jc[t]*ny[a])%mod; 54 ll ls2=(ls1*ny[b])%mod; 55 ll ls3=(((ls2*ny[n+a])%mod)*ny[m+b])%mod; 56 ans+=ls3; ans=ans%mod; 57 } 58 printf("%lld\n",ans); 59 } 60 return 0; 61 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<vector> 5 #define ll long long 6 #define maxx 100100 7 using namespace std; 8 int t,n,m,cnt,p; 9 ll ans; 10 ll jc[maxx],ny[maxx]; 11 vector <ll> modd; 12 vector <ll> jg; 13 void shai(int y) 14 { 15 int x=sqrt(y); 16 for(int i=2;i<=x;++i) 17 if(y%i==0) {modd.push_back((ll)i); y=y/i;} 18 if(y!=1) modd.push_back((ll)y); 19 } 20 ll ksm(ll a,ll b,ll c) 21 { 22 ll ans=1; a=a%c; 23 while(b) 24 { 25 if(b&1) ans=(ans*a)%c; 26 b=b>>1; a=(a*a)%c; 27 } 28 return ans; 29 } 30 ll Lucas(int n,int m,ll mm) 31 { 32 if(n<m) return 0; 33 if(n<mm&&m<mm) return (((jc[n]*ny[n-m])%mm)*ny[m])%mm; 34 return (Lucas(n/mm,m/mm,mm)*Lucas(n%mm,m%mm,mm))%mm; 35 } 36 int exgcd(int a,int b,int &x,int &y) 37 { 38 if(b==0) {x=1; y=0; return a;} 39 int gcd=exgcd(b,a%b,x,y); 40 int t=x; x=y; y=t-a/b*y; 41 return gcd; 42 } 43 ll China() 44 { 45 int x,y; ll a=0,m,n=1; 46 for(int i=0;i<modd.size();++i) n*=modd[i]; 47 for(int i=0;i<modd.size();++i) 48 { 49 m=n/modd[i]; 50 exgcd(modd[i],m,x,y); 51 a=(a+y*m*jg[i])%n; 52 } 53 if(a>0) return a; 54 else return a+n; 55 } 56 int main() 57 { 58 scanf("%d%d%d%d",&t,&p,&n,&m); 59 n=abs(n); m=abs(m); 60 if(((t-n-m)&1)!=0) {printf("0\n"); return 0;} 61 shai(p); 62 for(int i=0;i<modd.size();++i) 63 { 64 ll pp=modd[i],ans=0; 65 ll minnn=t; 66 jc[0]=1; 67 for(int j=1;j<=t;++j) 68 { 69 if(j>=pp) {minnn=pp-1; break;} 70 jc[j]=(jc[j-1]*j)%pp; 71 } 72 ny[minnn]=ksm(jc[minnn],pp-2,pp); 73 for(int j=minnn;j>=1;--j) ny[j-1]=(ny[j]*j)%pp; 74 for(int a=0;a<=(t-m-n)/2;++a) 75 { 76 int b=(t-n-m)/2-a; 77 ll C1=Lucas(2*a+n,a,pp)%pp; ll C2=Lucas(2*b+m,b,pp)%pp; 78 ll C3=Lucas(t,2*a+n,pp)%pp; 79 ll ls1=(C1*C2)%pp; ll ls2=(ls1*C3)%pp; 80 ans+=ls2; ans=ans%pp; 81 } 82 jg.push_back(ans); 83 } 84 printf("%lld\n",China()); 85 return 0; 86 }