4767: 两双手
来源:互联网 发布:微博上最恶心的公知 编辑:程序博客网 时间:2024/05/01 07:36
4767: 两双手
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 581 Solved: 167
[Submit][Status][Discuss]
Description
老W是个棋艺高超的棋手,他最喜欢的棋子是马,更具体地,他更加喜欢马所行走的方式。老W下棋时觉得无聊,便
决定加强马所行走的方式,更具体地,他有两双手,其中一双手能让马从(u,v)移动到(u+Ax,v+Ay)而另一双手能让
马从(u,v)移动到(u+Bx,v+By)。小W看见老W的下棋方式,觉得非常有趣,他开始思考一个问题:假设棋盘是个无限
大的二维平面,一开始马在原点(0,0)上,若用老W的两种方式进行移动,他有多少种不同的移动方法到达点(Ex,Ey
)呢?两种移动方法不同当且仅当移动步数不同或某一步所到达的点不同。老W听了这个问题,觉得还不够有趣,他
在平面上又设立了n个禁止点,表示马不能走到这些点上,现在他们想知道,这种情况下马有多少种不同的移动方
法呢?答案数可能很大,你只要告诉他们答案模(10^9+7)的值就行。
Input
第一行三个整数Ex,Ey,n分别表示马的目标点坐标与禁止点数目。
第二行四个整数Ax,Ay,Bx,By分别表示两种单步移动的方法,保证Ax*By-Ay*Bx≠0
接下来n行每行两个整数Sxi,Syi,表示一个禁止点。
|Ax|,|Ay|,|Bx|,|By| <= 500, 0 <= n,Ex,Ey <= 500
Output
仅一行一个整数,表示所求的答案。
Sample Input
4 4 1
0 1 1 0
2 3
0 1 1 0
2 3
Sample Output
40
HINT
Source
显然,能到达的每个点,到达时走到的步数是唯一确定的
相当于解一个二元一次方程组
那么按照起点出发经过的步数,将所有被禁止的点排序
定义f[i]:起点走到第i个点,中间不经过其它点的方案数
那么,f[i] = ways(1,i) - ∑f[k]*ways(k,i) (k = 2~i-1)
其中ways(x,y)即从点x走到点y的方案数,用组合数算一下就行了
预处理阶乘和逆元,O(n^2)
注意本题阶乘和逆元范围略大。。。。于是RE了好久
#include<iostream>#include<cstdio>#include<cstring>#include<vector>#include<queue>#include<algorithm>#include<cmath>#include<stack>using namespace std; const int N = 505;const int M = 5E5 + 50;typedef long long LL;const LL mo = 1000000007; struct Point{ int x,y,step; Point(){} Point(int x,int y,int step): x(x),y(y),step(step){} bool operator < (const Point &B) const {return step < B.step;}}p[N]; int n,Ex,Ey,tot,dx[2],dy[2],f[N],Fac[M],Inv[M]; inline int Mul(const LL &x,const LL &y) {return x * y % mo;}inline int Add(const int &x,const int &y) {return x + y < mo ? x + y : x + y - mo;}inline int Dec(const int &x,const int &y) {return x - y >= 0 ? x - y : x - y + mo;}inline int C(const int &N,const int &M) {return Mul(Fac[N],Mul(Inv[M],Inv[N - M]));} int ksm(int x,int y){ int ret = 1; for (; y; y >>= 1) { if (y & 1) ret = Mul(ret,x); x = Mul(x,x); } return ret;} inline int Ways(int x,int y){ int A = x * dy[1] - dx[1] * y; int B = dx[0] * dy[1] - dx[1] * dy[0]; if (A % B != 0) return 0; int a = A / B,b; if (dx[1] != 0) { b = x - dx[0] * a; if (b % dx[1] != 0) return 0; b /= dx[1]; } else { b = y - dy[0] * a; if (b % dy[1] != 0) return 0; b /= dy[1]; } return (a < 0 || b < 0) ? 0 : C(a + b,a);} inline int Calc(int x,int y){ int A = x * dy[1] - dx[1] * y; int B = dx[0] * dy[1] - dx[1] * dy[0]; if (A % B != 0) return -1; int a = A / B,b; if (dx[1] != 0) { b = x - dx[0] * a; if (b % dx[1] != 0) return -1; b /= dx[1]; } else { b = y - dy[0] * a; if (b % dy[1] != 0) return -1; b /= dy[1]; } return (a < 0 || b < 0) ? -1 : a + b;} int main(){ #ifdef DMC freopen("DMC.txt","r",stdin); #endif Fac[0] = 1; for (int i = 1; i < M; i++) Fac[i] = Mul(Fac[i - 1],i); Inv[M - 1] = ksm(Fac[M - 1],mo - 2); for (int i = M - 2; i >= 0; i--) Inv[i] = Mul(Inv[i + 1],i + 1); cin >> Ex >> Ey >> n >> dx[0] >> dy[0] >> dx[1] >> dy[1]; int End = Calc(Ex,Ey); if (End == -1) {cout << 0 << endl; return 0;} p[++tot] = Point(0,0,0); p[++tot] = Point(Ex,Ey,End); for (int i = 1; i <= n; i++) { int x,y; scanf("%d%d",&x,&y); int now = Calc(x,y); if (now == -1 || now >= End) continue; p[++tot] = Point(x,y,now); } sort(p + 1,p + tot + 1); f[1] = 1; for (int i = 2; i <= tot; i++) { f[i] = Ways(p[i].x,p[i].y); for (int j = 2; j < i; j++) f[i] = Dec(f[i],Mul(f[j],Ways(p[i].x - p[j].x,p[i].y - p[j].y))); } cout << f[tot] << endl; return 0;}
0 0
- 4767: 两双手
- bzoj4767: 两双手
- [容斥原理 DP] BZOJ 4767 两双手
- 双手
- 两年后无人驾驶将解放你的双手!
- [BZOJ Contest-2017省队十连测推广赛1·T3][BZOJ4767][DP][容斥原理]两双手
- 举起双手
- 解放程序员的双手
- 要学会双手弹钢琴
- 感恩的双手
- 双手双节棍中级套路
- 男人的双手
- 双手合十-------新年之开篇
- 双手托起一个门户网站
- 打字指法——双手
- 双手放在键盘上
- 合上双手,愿意做尘埃
- 就争讼 你的双手甩开
- pandas常用函数之diff
- C++第四次实验作业
- TensorFlow中的一个重要ops---MatMul的实现(二)
- MVC、MVP及MVVM的比较
- java 连接池
- 4767: 两双手
- Semaphores
- C++之STL和Boost
- CharSequence & String & StringBuilder & StringBuffer的简单理解
- BZOJ3876: [Ahoi2014]支线剧情 线性规划
- android AsyncTask介绍
- 记一次真机虚拟机互相ping不通
- 位运算符的基本用法
- 工业相机