bzoj 4767 两双手 - 动态规划 - 容斥原理

题目传送门

  传送门I

  传送门II

题目大意

  一个无限大的棋盘上有一只马,设马在某个时刻的位置为$(x, y)$, 每次移动可以将马移动到$(x + A_x, y + A_y)$或者$(x + B_x, y + B_y)$。棋盘上有$n$个禁止位置不能经过,问马从$(0, 0)$走到$(E_x, E_y)$的方案数。

  容斥是显然的。

  每确定经过$k$个禁止位置的方案数的容斥系数是$(-1)^{k}$。

  考虑带上容斥系数来动态规划,

  注意到去掉重复的禁止位置后,$(0, 0), (E_x, E_y)$以及禁止位置构成了一个DAG。

  容斥相当于求从$(0, 0)$到$(E_x, E_y)$的经过偶数个禁止位置的路径数减去经过奇数个禁止位置的路径数。

  直接动态规划计数就好了。

Code

  1 /**
  2  * bzoj
  3  * Problem#4767
  4  * Accepted
  5  * Time: 333ms
  6  * Memory: 10716k
  7  */
  8 #include <iostream>
  9 #include <cassert>
 10 #include <cstdlib>
 11 #include <cstdio>
 12 #include <queue>
 13 #include <set>
 14 using namespace std;
 15 typedef bool boolean;
 16
 17 const int N = 505, M = 1e9 + 7, D = 1e6 + 1;
 18
 19 int add(int a, int b) {
 20     return ((a += b) >= M) ? (a - M) : (a);
 21 }
 22
 23 int sub(int a, int b) {
 24     return ((a -= b) < 0) ? (a + M) : (a);
 25 }
 26
 27 int mul(int a, int b) {
 28     return a * 1ll * b % M;
 29 }
 30
 31 #define pii pair<int, int>
 32 #define fi first
 33 #define sc second
 34
 35 void exgcd(int a, int b, int& x, int& y) {
 36     if (!b)
 37         x = 1, y = 0;
 38     else {
 39         exgcd(b, a % b, y, x);
 40         y -= (a / b) * x;
 41     }
 42 }
 43
 44 int inv(int a, int n) {
 45     int x, y;
 46     exgcd(a, n, x, y);
 47     return (x < 0) ? (x + n) : (x);
 48 }
 49
 50 int fac[D], _fac[D];
 51
 52 inline void prepare() {
 53     fac[0] = 1;
 54     for (int i = 1; i < D; i++)
 55         fac[i] = mul(fac[i - 1], i);
 56     _fac[D - 1] = inv(fac[D - 1], M);
 57     for (int i = D; --i; )
 58         _fac[i - 1] = mul(_fac[i], i);
 59 }
 60
 61 int comb(int n, int m) {
 62     if (n < m)
 63         return 0;
 64     return mul(fac[n], mul(_fac[n - m], _fac[m]));
 65 }
 66
 67 boolean Solve(int a1, int b1, int a2, int b2, int c1, int c2, pii& sol) {
 68     int d = c2 * a1 - a2 * c1, f = a1 * b2 - a2 * b1;
 69     if (d % f)
 70         return false;
 71     sol.sc = d / f;
 72     if (a1) {
 73         d = c1 - b1 * sol.sc;
 74         if (d % a1)
 75             return false;
 76         sol.fi = d / a1;
 77     } else {
 78         d = c2 - b2 * sol.sc;
 79         if (d % a2)
 80             return false;
 81         sol.fi = d / a2;
 82     }
 83     return sol.fi >= 0 && sol.sc >= 0;
 84 }
 85
 86 int n, Ex, Ey;
 87 int Ax, Ay, Bx, By;
 88 pii ps[N];
 89 int deg[N];
 90 set<pii> s;
 91 boolean g[N][N];
 92
 93 //#define _DEBUG_
 94 #ifdef _DEBUG_
 95 FILE* fin = fopen("6.in", "r");
 96 #else
 97 FILE* fin = stdin;
 98 #endif
 99
100 boolean Solve(pii ps, pii pt, pii& sol) {
101     return Solve(Ax, Bx, Ay, By, pt.fi - ps.fi, pt.sc - ps.sc, sol);
102 }
103
104 inline void init() {
105     fscanf(fin, "%d%d%d", &Ex, &Ey, &n);
106     fscanf(fin, "%d%d%d%d", &Ax, &Ay, &Bx, &By);
107     for (int i = 1; i <= n; i++) {
108         fscanf(fin, "%d%d", &ps[i].fi, &ps[i].sc);
109         if (s.count(ps[i]))
110             i--, n--;
111         s.insert(ps[i]);
112     }
113 }
114
115 int f[N];
116 queue<int> que;
117 inline void solve() {
118     int t = n + 1;
119     pii p, S(0, 0), T(Ex, Ey);
120     ps[0] = S, ps[t] = T;
121     for (int i = 1; i <= n; i++)
122         if (Solve(S, ps[i], p))
123             g[0][i] = true, deg[i]++;
124     for (int i = 1; i <= n; i++)
125         for (int j = 1; j <= n; j++)
126             if ((i ^ j) && Solve(ps[i], ps[j], p))
127                 g[i][j] = true, deg[j]++, assert(!g[j][i]);
128     for (int i = 1; i <= n; i++)
129         if (Solve(ps[i], T, p))
130             g[i][t] = true, deg[t]++;
131     if (Solve(S, T, p))
132         g[0][t] = true, deg[t]++;
133
134     f[0] = 1;
135     que.push(0);
136     while (!que.empty()) {
137         int e = que.front();
138         que.pop();
139         for (int i = 1; i <= t; i++) {
140             if (!g[e][i])
141                 continue;
142             Solve(ps[e], ps[i], p);
143             if (i && i != t)
144                 f[i] = sub(f[i], mul(f[e], comb(p.fi + p.sc, p.fi)));
145             else
146                 f[i] = add(f[i], mul(f[e], comb(p.fi + p.sc, p.fi)));
147             if (!(--deg[i]))
148                 que.push(i);
149         }
150     }
151 //    for (int i = 1; i <= n; i++)
152 //        assert(!deg[i]);
153 //        if (deg[i])
154 //            cerr << i << " " << ps[i].fi << " " << ps[i].sc << endl;
155     printf("%d\n", f[t]);
156 }
157
158 int main() {
159     prepare();
160     init();
161     solve();
162     return 0;
163 }

原文地址:https://www.cnblogs.com/yyf0309/p/9901239.html

时间: 2024-10-29 10:39:35

bzoj 4767 两双手 - 动态规划 - 容斥原理的相关文章

[BZOJ 4767]两双手(组合数学+Dp)

Description 老W是个棋艺高超的棋手,他最喜欢的棋子是马,更具体地,他更加喜欢马所行走的方式.老W下棋时觉得无聊,便 决定加强马所行走的方式,更具体地,他有两双手,其中一双手能让马从(u,v)移动到(u+Ax,v+Ay)而另一双手能让 马从(u,v)移动到(u+Bx,v+By).小W看见老W的下棋方式,觉得非常有趣,他开始思考一个问题:假设棋盘是个无限 大的二维平面,一开始马在原点(0,0)上,若用老W的两种方式进行移动,他有多少种不同的移动方法到达点(Ex,Ey )呢?两种移动方法不

BZOJ 4767 两双手

题解: 发现这种题目虽然可以想出来,但磕磕碰碰得想挺久的 根据数学可以知道组成方案是唯一的(集合) 然后发现每个使用的大小可能是接近n^2的 直接dp(n^4)是过不了的 那么先观察观察 我们可以把每个障碍点的表示也搞出来 这样就变成了一张网格图求起点到终点的方案数 然后考虑一下容斥,枚举第一个经过的障碍点是谁(之后就随便走了) 然后发现做这个的时候在不断递归 那可以直接按照x,y排个序 依次递推过去 原文地址:https://www.cnblogs.com/yinwuxiao/p/888387

BZOJ 2024 SHOI2009 舞会 动态规划+容斥原理+高精度

题目大意:给定两个序列,求有多少个匹配满足a[i]<b[i]的对数不超过k对 见http://blog.csdn.net/popoqqq/article/details/44514113 高精度已废... #include <cstdio> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> #define M 202 using na

bzoj4767两双手 容斥+组合

4767: 两双手 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 684  Solved: 208[Submit][Status][Discuss] Description 老W是个棋艺高超的棋手,他最喜欢的棋子是马,更具体地,他更加喜欢马所行走的方式.老W下棋时觉得无聊,便 决定加强马所行走的方式,更具体地,他有两双手,其中一双手能让马从(u,v)移动到(u+Ax,v+Ay)而另一双手能让 马从(u,v)移动到(u+Bx,v+By).小W看见老

Bzoj4767 两双手

Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 553  Solved: 160 Description 老W是个棋艺高超的棋手,他最喜欢的棋子是马,更具体地,他更加喜欢马所行走的方式.老W下棋时觉得无聊,便 决定加强马所行走的方式,更具体地,他有两双手,其中一双手能让马从(u,v)移动到(u+Ax,v+Ay)而另一双手能让 马从(u,v)移动到(u+Bx,v+By).小W看见老W的下棋方式,觉得非常有趣,他开始思考一个问题:假设棋盘是个无限 大的

洛谷P1450 [HAOI2008]硬币购物 动态规划 + 容斥原理

洛谷P1450 [HAOI2008]硬币购物 动态规划 + 容斥原理 1.首先我们去掉限制 假设 能够取 无数次 也就是说一开始把他当做完全背包来考虑 离线DP 预处理 复杂度 4*v 用f[ i ] 表示 空间 为 i 的方案数 答案ans 其实就是所有方案 - 所有超过限制的方案 限制指的就是题目中限制 某个硬币有几枚 然后所有超过限制的方案用容斥来做 所有超过限制的方案 要减 == -1 超过限制的方案 - 2 超过限制的方案 - 3 超过限制的方案 - 4 超过限制的方案 + 1和2 超

【题解/总结】两双手(格路问题)/格路问题的某一本质

[题解]两双手(格路问题) 题目大意:求从\((0,0)\)到\((Ex,Ey)\)不经过给定障碍点的方案数.你每次移动只能是加上向量\(e_1\)或者向量\(e_2\),\(e_1,e_2\)中的基底都是整数. 考虑转化一下这个问题,从某个点走到在他右上角的某点需要加上\(ae_1+be_2\),这样我们就可以解出\(a,b\).我们把\((a,b)\)拿出来建立新的坐标系,就变成了简单的格路问题了.结合[题解]CF559C C. Gerald and Giant Chess(容斥+格路问题)

BZOJ 3622 已经没有什么好害怕的了 动态规划+容斥原理

题目大意:给定两个长度为n个序列,保证这2n个数字两两不同,求有多少匹配满足a[i]>b[i]的数对数比a[i]<b[i]的数对数多k もう何も怖くない 题解:http://www.cnblogs.com/dyllalala/p/3900077.html OTZ 神思路根本就是想不到啊QAQ でも...もう何も怖くない...(大雾 此外我们可以引入一下WTY公式: C[i][j]=C[i-1][j]*C[i-1][j-1] ...脑残怎么治啊... #include <cstdio>

Leetcode139题Word Break的两种动态规划解法

由leetcode139题Word Break产生的对动态规划的一点思考. 题目要求是这样的: Given a string s and a dictionary of words dict,determine if s can be segmented into a space-separatedsequence of one or more dictionary words. For example, given s = "leetcode", dict = ["leet