题意:给出一个矩阵棋盘,大小不超过10^5。上面有n个非法点(n<=2000)不可以踩,从左上角开始走到右下角,每次只能向下或者向右移动。问有多少种走法。结果对10^9+7取模。

分析:

组合数学DP

设dp[i]表示从左上角开始不经过非法点走到第i个非法点有多少种方法。

dp[i]=num(s, point[i]) - sigma( dp[j] * num(point[j], point[i]) );

其中,sigma表示加和,s是起始点,j遍历所有在点i左上方的点。num(a,b)表示从a到b有多少种走法(不用避开非法点)。

num可以直接用组合数表示,即c(x+y-2, x -1)。

这道题涉及到了较大的组合数取模问题。求大组合数方法如下:

可以先处理出所有1~n的阶乘,存入数组。factorial[i]表示i的阶乘取模后的结果。

并求出所有的阶乘在该模运算下的逆元,存入数组。inverse[i]表示factorial[i]的逆元。

c(n,m)原本等于( n! / (n-m)! ) / m!,现在用这两个数组可以表示为factorial[n] * inverse[n-m] * inverse[m]。

模板如下:

#define LL long long

const int MAX_M = (int)(2e5) + ;
const int MOD = (int)(1e9) + ; LL multi_mod(LL a, LL b, LL c)
{ //返回(a*b) mod c,a,b,c<2^63
a %= c;
b %= c;
LL ret = ;
while (b)
{
if (b & )
ret = (ret + a) % c;
a <<= ;
a %= c;
b >>= ;
}
return ret;
} LL pow_mod(LL x, LL n, LL mod)
{ //返回x^n mod c ,非递归版
x %= mod;
if (n == )
return x;
LL ret = ;
while (n)
{
if (n & )
ret = multi_mod(ret, x, mod);
n >>= ;
x = multi_mod(x, x, mod);
}
return ret;
} long long get_inverse(long long a)
{
return pow_mod(a, MOD - , MOD);
} long long factorial[MAX_M];
long long inverse[MAX_M]; void init_comb(int n)
{
factorial[] = ;
for (int i = ; i <= n; i++)
{
factorial[i] = factorial[i - ] * i;
factorial[i] %= MOD;
}
inverse[n] = get_inverse(factorial[n]);
for (int i = n - ; i >= ; i--)
{
inverse[i] = inverse[i + ] * (i + );
inverse[i] %= MOD;
}
} long long comb(long long a, long long b)
{
long long ret = factorial[a] * inverse[a - b];
ret %= MOD;
ret *= inverse[b];
ret %= MOD;
return ret;
}

求逆元可以使用扩展欧几里德算法,但是比较麻烦。这次我使用了一个更为简单的算法,费马小定理。

即:当p是质数且a和p互质,那么a^(p-1)=1 (mod p)

而逆元的定义是x * y=1 (mod p)则y是x的逆元。令x=a,且a与p互质,则由a^(p-1)=1 (mod p)可得:y=a^(p-2)。

对于求a的逆元这个问题,a<p且p是质数,自然可以利用上面的结论,a的逆元就是a^(p-2)。

求逆元模板如下:

LL multi_mod(LL a, LL b, LL c)
{ //返回(a*b) mod c,a,b,c<2^63
a %= c;
b %= c;
LL ret = ;
while (b)
{
if (b & )
ret = (ret + a) % c;
a <<= ;
a %= c;
b >>= ;
}
return ret;
} LL pow_mod(LL x, LL n, LL mod)
{ //返回x^n mod c ,非递归版
x %= mod;
if (n == )
return x;
LL ret = ;
while (n)
{
if (n & )
ret = multi_mod(ret, x, mod);
n >>= ;
x = multi_mod(x, x, mod);
}
return ret;
} long long get_inverse(long long a)
{
return pow_mod(a, MOD - , MOD);
}

本题代码如下:

#include <cstdio>
#include <algorithm>
using namespace std; #define d(x)
#define LL long long const int MAX_N = ;
const int MAX_M = (int)(2e5) + ;
const int MOD = (int)(1e9) + ; int row_num, col_num;
int n;
pair<int, int> point[MAX_N];
long long dp[MAX_N]; void input()
{
scanf("%d%d%d", &row_num, &col_num, &n);
for (int i = ; i < n; i++)
{
int a, b;
scanf("%d%d", &a, &b);
point[i] = make_pair(a, b);
}
point[n++] = make_pair(row_num, col_num);
point[n++] = make_pair(, );
for (int i = ; i < n; i++)
{
point[i].first--;
point[i].second--;
}
} LL multi_mod(LL a, LL b, LL c)
{ //返回(a*b) mod c,a,b,c<2^63
a %= c;
b %= c;
LL ret = ;
while (b)
{
if (b & )
ret = (ret + a) % c;
a <<= ;
a %= c;
b >>= ;
}
return ret;
} LL pow_mod(LL x, LL n, LL mod)
{ //返回x^n mod c ,非递归版
x %= mod;
if (n == )
return x;
LL ret = ;
while (n)
{
if (n & )
ret = multi_mod(ret, x, mod);
n >>= ;
x = multi_mod(x, x, mod);
}
return ret;
} long long get_inverse(long long a)
{
return pow_mod(a, MOD - , MOD);
} long long factorial[MAX_M];
long long inverse[MAX_M]; void init_comb(int n)
{
factorial[] = ;
for (int i = ; i <= n; i++)
{
factorial[i] = factorial[i - ] * i;
factorial[i] %= MOD;
}
inverse[n] = get_inverse(factorial[n]);
for (int i = n - ; i >= ; i--)
{
inverse[i] = inverse[i + ] * (i + );
inverse[i] %= MOD;
}
} long long comb(long long a, long long b)
{
long long ret = factorial[a] * inverse[a - b];
ret %= MOD;
ret *= inverse[b];
ret %= MOD;
return ret;
} int main()
{
init_comb();
input();
sort(point, point + n);
dp[] = ;
for (int i = ; i < n; i++)
{
dp[i] = comb(point[i].first + point[i].second, point[i].second);
for (int j = ; j < i; j++)
{
if (point[j].first <= point[i].first && point[j].second <= point[i].second)
{
long long a = point[i].first - point[j].first;
a += point[i].second - point[j].second; long long b = point[i].second - point[j].second; dp[i] -= (dp[j] * comb(a, b)) % MOD;
dp[i] += MOD;
dp[i] %= MOD;
}
}
d(printf("dp[%d] = %d\n", i, (int)dp[i]));
}
printf("%d\n", (int)dp[n - ]);
return ;
}

ccf559c的更多相关文章

随机推荐

  1. Mongodb3.0.6副本集+分片学习笔记

    一.使用问题记录 1. mongodb3.0.6使用mongostat参数 >./mongostat -h 127.0.0.1:27017 -u root -p 123456 /authenti ...

  2. MySQL里的found_row()与row_count()的解释及用法

    MySQL中有两个函数来计算上一条语句影响了多少行,不同于SqlServer/Oracle,不要因为此方面的差异而引起功能问题   出处:mysqlpub.com MySQL中有两个函数来计算上一条语 ...

  3. fopen()和fclose()的用法

    fopen()和fclose()的用法 1.fopen()函数的用法 fopen函数用于打开文件, 其调用格式为: FILE *fopen(char *filename, *type); fopen( ...

  4. GDB 修改当前判断函数的返回值(即修改寄存器的值)的方法

    工作中遇到的问题: 在GDB调试时要进入下边该判断后边的函数,而m_EtherDecode.Chk_MakeSure_IP_Pkt(pPacket,dwPacketLen)的返回值是false,所以需 ...

  5. sql server cpu占用过高优化

    SQLSERVER排查CPU占用高的情况 今天中午,有朋友叫我帮他看一下数据库,操作系统是Windows2008R2 ,数据库是SQL2008R2 64位 64G内存,16核CPU 硬件配置还是比较高 ...

  6. python文件I/O(转)

    Python 文件I/O 本章只讲述所有基本的的I/O函数,更多函数请参考Python标准文档. 打印到屏幕 最简单的输出方法是用print语句,你可以给它传递零个或多个用逗号隔开的表达式.此函数把你 ...

  7. Ansible简明使用手册

            Ansible使用简明手册 1.简介 ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet.cfengine.chef.func.fabric ...

  8. Mahout 介绍

    1.Hbase+k-means  (G级别) 2.k-means+mr (T级别) 1. 2.canopy 2.贝叶斯算法 决策,分类,文档分类 3.推荐系统 4.图书推荐系统 1.需求 付完款的用户 ...

  9. Windows 7 共享文件夹 给 VirtualBox 中的 Ubuntu 14

    操作步骤如下: 1.打开虚拟机中的 Ubuntu 系统: 2.安装“增强工具” 设备 -> 安装增强工具 3.设置“共享文件夹” 控制 -> 设置 -> 添加共享文件夹 ->  ...

  10. Ubuntu 14 如何解压 .zip、.rar 文件?

    .zip 和 .rar 是Windows下常用的压缩文件,在Ubuntu中如何解压? [解压.zip文件] Ubuntu中貌似已经安装了unzip软件,解压命令如下: unzip ./FileName ...