HDU 5794 A Simple Chess(数论)

Love The Way You Lie 2022-09-25 02:30 262阅读 0赞

There is a n×m board, a chess want to go to the position
(n,m) from the position (1,1).The chess is able to go to position (x2,y2) from the position (x1,y1), only and if only x1,y1,x2,y2 is satisfied that (x2−x1)2+(y2−y1)2=5, x2>x1, y2>y1.

以上是部分题目无用


题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5794

题目大意:给你一个起点(1,1),每次只能马字形行走,问到达最终的终点有多少种走法,中途会有一些障碍不能行走。

解题思路:
很容易得出的结论:最终的答案就是不经过障碍能达到终点的所有方法,可以用起点到终点的总路径数减去会经过障碍的路径数。

那么如何求出起点到终点会经过障碍的方法数,考虑将给的障碍物按x从小到大排序。

我们记起点到点i不经过障碍物的路径数为num[i]
记点i到点j的总路径数(可以经过障碍物)为cal(i,j)
记起点为begin,终点为end。

对于第一个障碍物i,起点到i不会经过其它障碍物,所以i阻挡的路径就是起点到i的路径数乘以i到终点的路径数(即num[i]*cal(begin,i))。

对于第二个障碍物i+1,它挡住的路径会受第一个的影响,新挡住的路径就是num[i+1]*cal(i+1,end)
num[i+1]=cal(begin,i)-num[i]*cal(i,i+1)。

对于第三个障碍物i+2,它新挡住的路径就是num[i+2]*cal(i+2,end),而num[i+2]=cal(begin,i+2)-num[i]*cal(i,i+2)-num[i+1]*cal(i+1,i+2)。

对于第n个障碍物,它新挡住路径是num[n]*cal(n,end),而num[n]=cal(begin,n)-num[i]*cal(i,n)-num[i+1]*cal(i+1,n)-num[i+2]*cal(i+2,n)- - ->num[n-1]*cal(n-1,n)。

把每个点新挡住的路径加起来就可以得到所有的被挡住的路径数,用cal(begin,end)减去其即可。

AC代码:

  1. #include <iostream>
  2. #include <cstdlib>
  3. #include <cstdio>
  4. #include <algorithm>
  5. #include <string>
  6. #include <cstring>
  7. #include <map>
  8. #include <vector>
  9. #include <cmath>
  10. #include <stack>
  11. #include <queue>
  12. #include<cmath>
  13. using namespace std;
  14. typedef long long ll;
  15. const ll mod=110119;
  16. const ll maxn=120000;
  17. ll fact[maxn];
  18. long long n,m,r;
  19. ll mod_comb(ll n,ll k, ll p);
  20. ll mod_fact(ll n,ll p, ll &e);
  21. ll mod_inverse(ll a,ll m);
  22. void init();//求组合数的四个函数,没用卢卡斯
  23. struct P
  24. {
  25. ll x;
  26. ll y;
  27. };
  28. vector<P> vec;
  29. bool judge(P p1,P p2)//判断两点是否能到达
  30. {
  31. ll x=p2.x-p1.x;
  32. ll y=p2.y-p1.y;
  33. if(2*x-y<0||2*y-x<0) return false;
  34. ll x1=(2*x-y)/3;
  35. ll y1=(2*y-x)/3;
  36. if((x1*3)==(2*x-y)&&(y1*3)==(2*y-x)) return true;
  37. return false;
  38. }
  39. ll cal(P p1 ,P p2)//计算两点间的所有方法数
  40. {
  41. if(!judge(p1,p2)) return 0;
  42. ll x=p2.x-p1.x;
  43. ll y=p2.y-p1.y;
  44. ll x1=(2*x-y)/3;
  45. ll y1=(2*y-x)/3;
  46. if(x1<y1) swap(x1,y1);
  47. y1++;
  48. //向下走x1步和向右走y1步即可排列组合可计算不同走法
  49. return mod_comb(x1+y1-1,y1-1,mod)%mod;
  50. }
  51. ll abss(ll x)
  52. {
  53. if(x<0) return -x;
  54. return x;
  55. }
  56. bool cmp(P &p1,P &p2)//给点按x排序
  57. {
  58. if(p1.x==p2.x) return p1.y<p2.y;
  59. return p1.x<p2.x;
  60. }
  61. int main()
  62. {
  63. int cas=1;
  64. init();
  65. while(scanf("%I64d%I64d%I64d",&n,&m,&r)!=EOF)
  66. {
  67. vec.clear();
  68. P p2,p1;
  69. p2.x=n;
  70. p2.y=m;
  71. p1.x=1;
  72. p1.y=1;
  73. for(ll i=0; i<r; i++)
  74. {
  75. P p3;
  76. scanf("%I64d%I64d",&p3.x,&p3.y);
  77. if(judge(p1,p3)&&judge(p3,p2)) vec.push_back(p3);
  78. }
  79. printf("Case #%d: ",cas++);
  80. if(!judge(p1,p2))
  81. {
  82. printf("0\n");
  83. continue;
  84. }
  85. sort(vec.begin(),vec.end(),cmp);
  86. ll ans=cal(p1,p2);
  87. ll num[150];//存储起点到点i不经过障碍的路径数
  88. for(int i=0; i<vec.size(); i++)
  89. {
  90. ll ans1=0;
  91. for(int j=0;j<i;j++)
  92. {
  93. ans1=(ans1+cal(vec[j],vec[i])*num[j])%mod;
  94. }
  95. num[i]=(cal(p1,vec[i])-ans1+mod)%mod;
  96. ans=((ans-num[i]*cal(vec[i],p2)%mod)+mod)%mod;
  97. }
  98. printf("%I64d\n",ans);
  99. }
  100. return 0;
  101. }
  102. void init()
  103. {
  104. fact[0]=1;
  105. ll res=1;
  106. for(ll i=1; i<maxn; i++)
  107. {
  108. res=(res*i)%mod;
  109. fact[i]=res%mod;
  110. }
  111. }
  112. ll extgcd(ll a,ll b, ll &x,ll& y)
  113. {
  114. ll d=a;
  115. if(b!=0)
  116. {
  117. d=extgcd(b,a%b,y,x);
  118. y-=(a/b)*x;
  119. }
  120. else
  121. {
  122. x=1;
  123. y=0;
  124. }
  125. return d;
  126. }
  127. ll mod_inverse(ll a,ll m)
  128. {
  129. ll x,y;
  130. extgcd(a,m,x,y);
  131. return (m+x%m)%m;
  132. }
  133. ll mod_fact(ll n,ll p, ll &e)
  134. {
  135. e=0;
  136. if(n==0) return 1;
  137. ll res=mod_fact(n/p,p,e);
  138. e+=n/p;
  139. if(n/p%2!=0) return res*(p-fact[n%p])%p;
  140. return res*fact[n%p]%p;
  141. }
  142. ll mod_comb(ll n,ll k, ll p)
  143. {
  144. if(n<0||k<0||n<k)return 0;
  145. ll e1,e2,e3;
  146. ll a1=mod_fact(n,p,e1);
  147. ll a2=mod_fact(k,p,e2);
  148. ll a3=mod_fact(n-k,p,e3);
  149. if(e1>e2+e3)return 0;
  150. return a1*mod_inverse(a2*a3%p,p)%p;
  151. }

发表评论

表情:
评论列表 (有 0 条评论,262人围观)

还没有评论,来说两句吧...

相关阅读

    相关 HDU_2580 A simple stone game

    刚开始看这一题时,就知道这根本不是一道简单题(对当时没学K倍动态减法的我来说),因为前几天刚做完一道斐波那契额数列的博弈而且它仅仅是这道题k=2的一个特例而已-\_-|||。

    相关 HDU(1851) A Simple Game (博弈)

    任给N堆石子,两人轮流从任一堆中任取(每次只能取自一堆),规定每方每次最多取K颗,取最后一颗石子的一方获胜.问先取的人如何获胜? 巴什博奕和尼姆博弈的综合。 令Bi=Mi