bzoj 4767 两双手 - 动态规划 - 容斥原理
题目传送门
题目大意
一个无限大的棋盘上有一只马,设马在某个时刻的位置为$(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
/**
* bzoj
* Problem#4767
* Accepted
* Time: 333ms
* Memory: 10716k
*/
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <set>
using namespace std;
typedef bool boolean; const int N = , M = 1e9 + , D = 1e6 + ; int add(int a, int b) {
return ((a += b) >= M) ? (a - M) : (a);
} int sub(int a, int b) {
return ((a -= b) < ) ? (a + M) : (a);
} int mul(int a, int b) {
return a * 1ll * b % M;
} #define pii pair<int, int>
#define fi first
#define sc second void exgcd(int a, int b, int& x, int& y) {
if (!b)
x = , y = ;
else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
} int inv(int a, int n) {
int x, y;
exgcd(a, n, x, y);
return (x < ) ? (x + n) : (x);
} int fac[D], _fac[D]; inline void prepare() {
fac[] = ;
for (int i = ; i < D; i++)
fac[i] = mul(fac[i - ], i);
_fac[D - ] = inv(fac[D - ], M);
for (int i = D; --i; )
_fac[i - ] = mul(_fac[i], i);
} int comb(int n, int m) {
if (n < m)
return ;
return mul(fac[n], mul(_fac[n - m], _fac[m]));
} boolean Solve(int a1, int b1, int a2, int b2, int c1, int c2, pii& sol) {
int d = c2 * a1 - a2 * c1, f = a1 * b2 - a2 * b1;
if (d % f)
return false;
sol.sc = d / f;
if (a1) {
d = c1 - b1 * sol.sc;
if (d % a1)
return false;
sol.fi = d / a1;
} else {
d = c2 - b2 * sol.sc;
if (d % a2)
return false;
sol.fi = d / a2;
}
return sol.fi >= && sol.sc >= ;
} int n, Ex, Ey;
int Ax, Ay, Bx, By;
pii ps[N];
int deg[N];
set<pii> s;
boolean g[N][N]; //#define _DEBUG_
#ifdef _DEBUG_
FILE* fin = fopen("6.in", "r");
#else
FILE* fin = stdin;
#endif boolean Solve(pii ps, pii pt, pii& sol) {
return Solve(Ax, Bx, Ay, By, pt.fi - ps.fi, pt.sc - ps.sc, sol);
} inline void init() {
fscanf(fin, "%d%d%d", &Ex, &Ey, &n);
fscanf(fin, "%d%d%d%d", &Ax, &Ay, &Bx, &By);
for (int i = ; i <= n; i++) {
fscanf(fin, "%d%d", &ps[i].fi, &ps[i].sc);
if (s.count(ps[i]))
i--, n--;
s.insert(ps[i]);
}
} int f[N];
queue<int> que;
inline void solve() {
int t = n + ;
pii p, S(, ), T(Ex, Ey);
ps[] = S, ps[t] = T;
for (int i = ; i <= n; i++)
if (Solve(S, ps[i], p))
g[][i] = true, deg[i]++;
for (int i = ; i <= n; i++)
for (int j = ; j <= n; j++)
if ((i ^ j) && Solve(ps[i], ps[j], p))
g[i][j] = true, deg[j]++, assert(!g[j][i]);
for (int i = ; i <= n; i++)
if (Solve(ps[i], T, p))
g[i][t] = true, deg[t]++;
if (Solve(S, T, p))
g[][t] = true, deg[t]++; f[] = ;
que.push();
while (!que.empty()) {
int e = que.front();
que.pop();
for (int i = ; i <= t; i++) {
if (!g[e][i])
continue;
Solve(ps[e], ps[i], p);
if (i && i != t)
f[i] = sub(f[i], mul(f[e], comb(p.fi + p.sc, p.fi)));
else
f[i] = add(f[i], mul(f[e], comb(p.fi + p.sc, p.fi)));
if (!(--deg[i]))
que.push(i);
}
}
// for (int i = 1; i <= n; i++)
// assert(!deg[i]);
// if (deg[i])
// cerr << i << " " << ps[i].fi << " " << ps[i].sc << endl;
printf("%d\n", f[t]);
} int main() {
prepare();
init();
solve();
return ;
}
bzoj 4767 两双手 - 动态规划 - 容斥原理的更多相关文章
- bzoj 4767: 两双手 组合 容斥
题目链接 bzoj4767: 两双手 题解 不共线向量构成一组基底 对于每个点\((X,Y)\)构成的向量拆分 也就是对于方程组 $Ax * x + Bx * y = X $ \(Ay * x + B ...
- BZOJ 4767: 两双手 [DP 组合数]
传送门 题意: 给你平面上两个向量,走到指定点,一些点不能经过,求方案数 煞笔提一开始被题面带偏了一直郁闷为什么方案不是无限 现在精简的题意.....不就是$bzoj3782$原题嘛,还不需要$Luc ...
- BZOJ.4767.两双手(组合 容斥 DP)
题目链接 \(Description\) 棋盘上\((0,0)\)处有一个棋子.棋子只有两种走法,分别对应向量\((A_x,A_y),(B_x,B_y)\).同时棋盘上有\(n\)个障碍点\((x_i ...
- BZOJ 4767 两双手
题解: 发现这种题目虽然可以想出来,但磕磕碰碰得想挺久的 根据数学可以知道组成方案是唯一的(集合) 然后发现每个使用的大小可能是接近n^2的 直接dp(n^4)是过不了的 那么先观察观察 我们可以把每 ...
- 【BZOJ】4767: 两双手【组合数学】【容斥】【DP】
4767: 两双手 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1057 Solved: 318[Submit][Status][Discuss] ...
- bzoj4767两双手 容斥+组合
4767: 两双手 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 684 Solved: 208[Submit][Status][Discuss] ...
- 【BZOJ4767】两双手(动态规划,容斥)
[BZOJ4767]两双手(动态规划,容斥) 题面 BZOJ 题解 发现走法只有两种,并且两维坐标都要走到对应的位置去. 显然对于每个确定的点,最多只有一种固定的跳跃次数能够到达这个点. 首先对于每个 ...
- BZOJ4767: 两双手【组合数学+容斥原理】
Description 老W是个棋艺高超的棋手,他最喜欢的棋子是马,更具体地,他更加喜欢马所行走的方式.老W下棋时觉得无聊,便决定加强马所行走的方式,更具体地,他有两双手,其中一双手能让马从(u,v) ...
- Bzoj 1042: [HAOI2008]硬币购物 容斥原理,动态规划,背包dp
1042: [HAOI2008]硬币购物 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1747 Solved: 1015[Submit][Stat ...
随机推荐
- Linux(CentOs 7)系统重装笔记(一)
参考文章: https://www.jb51.net/article/95263.htm https://blog.csdn.net/JackLiu16/article/details/7988182 ...
- this在java中的用法
this在java中的用法 1.使用this关键字引用成员变量 作用:解决成员变量与参数或局部变量命名冲突的问题 public class Dog { String name; public Dog( ...
- 剑指offer-机器人的运动范围
题目描述 地上有一个m行和n列的方格.一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子. 例如,当k为18时,机器人能 ...
- Vue.js——快速入门Vuex
一. 什么是Vuex? Vuex是一个专门为Vue.js应用程序开发的状态管理模式, 它采用集中式存储管理所有组件的公共状态, 并以相应的规则保证状态以一种可预测的方式发生变化. 上图中绿色虚线包裹起 ...
- 10个用于处理日期和时间的 Python 库
Python本身提供了处理时间日期的功能,也就是datetime标准库.除此之外,还有很多优秀的第三方库可以用来转换日期格式,格式化,时区转化等等.今天就给大家分享10个这样的Python库. 上期入 ...
- 一小时上手Java 8新特性
一小时上手Java 8新特性 本文摘译自 https://www.journaldev.com/2389/java-8-features-with-examples,并做了适当增补. Iterable ...
- Cocos Creator_继承组件单例
前言 单例,在游戏开发中是比较常用的功能,全局唯一,可以在任何地方直接获取, 省去了方法赋值 或者 属性面板拖动的麻烦. 普通单例_饿汉模式 不管有没调用,一开始就创建单例 1 // Singleto ...
- 运维自动化之系统部署 cobbler(三)
cobbler 介绍 Cobbler: 快速网络安装linux操作系统的服务,支持众多的Linux发行版:Red Hat.Fedora.CentOS.Debian.Ubuntu和SuSE,也可以支持网 ...
- Chrome Inspect调试微信出现空白页面的解决方法
首先,需要打开手机的USB调试和微信的TBS 调试开关. 如果不打开TBS开关,Inspect时会检测不到任何微信的H5页面 使用微信扫码下方二维码,打开TBS调试开关: 普通网页: 小程序: 微信扫 ...
- JavaIO流——简单对文件的写入及读取(三)
已经讲了写入和读取了,那么想要把一个文件的内容复制到另一个文件呢 不说太多,直接见代码 public static void copyFile(String srcFilename, String d ...