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

Posted yyf0309

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
", f[t]);
156 }
157 
158 int main() {
159     prepare();
160     init();
161     solve();
162     return 0;
163 }

 

以上是关于bzoj 4767 两双手 - 动态规划 - 容斥原理的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 4767 两双手

[BZOJ4767]两双手

Bzoj4767 两双手

bzoj4767 两双手

BZOJ[4767] 两双手

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