51 nod 1486 大大走格子 容斥原理+组合数学+dp

2023-12-07 03:58

本文主要是介绍51 nod 1486 大大走格子 容斥原理+组合数学+dp,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1486 大大走格子
题目来源: CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题
有一个h行w列的棋盘,里面有一些格子是不能走的,每次只能往下、往右走。现在要求从左上角走到右下角的方案数。
Input
单组测试数据。
第一行有三个整数h, w, n(1 ≤ h, w ≤ 10^5, 1 ≤ n ≤ 2000),表示棋盘的行和列,还有不能走的格子的数目。
接下来n行描述格子,第i行有两个整数ri, ci (1 ≤ ri ≤ h, 1 ≤ ci ≤ w),表示格子所在的行和列。
输入保证起点和终点不会有不能走的格子。
Output
输出答案对1000000007取余的结果。
Input示例
3 4 2
2 2
2 3

Output示例

2

题解:很容易想到这是一道dp的题,很容易写出方程dp【i】【j】=dp【i-1】【j】+dp【i】【j-1】。这可是杨辉三角一般的方程啊,好啦,结论出来啦,从左上走到右下直接=C(m+n-2,m-1)。但这道题有一些格子不能走,那么补集思想就来啦,用总方案数-经过非法格点的方案数=合法方案数。非法格点方案数=(经过一个非法格点方案数-进过两个非法格点方案数+经过三个非法格点方案数……)(容斥原理:奇加偶减),现在难点就在怎么求经过i个非法格点方案数。通过之前的dp已经求出左上到任意格点的距离,包括非法格点,那么可以通过非法格点之间的来找。先将非法格点按x,y坐标排序,目的是枚举走到非法格点i可以经过的其他非法格点,找出两个非法格i,j(i 在j之后)点之间的路径条数num,则这num条路径就不合法,f【i】表示走大非法节点i不经过其他非法节点的数量,f【i】=f【i】-f【j】*num,因为是层层往后推的,所以讲(n,m)也视为非法节点,则答案为dp【(n,m)】。为什么这是容斥原理呢,可以证明:f【i】-f【j】*num,其中可以把f【j】拆成f【j】-f【k】*num~,因为是减法,所以最终推到终点的时候满足奇加偶减。

总结:容斥原理可以是明显的,也可以是不明显的,比如本题就不太明显,以后要一个阶段一个阶段的分析,找出阶段之间的重复部分,合理容斥。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define N 2100000
#define x1 (point[i].x)
#define y1 (point[i].y)
#define x2 (point[j].x)
#define y2 (point[j].y)
#define calc(m,n) (re[m]*inv[n]%mod*inv[m-n]%mod)%mod
#define rdx(i) point[i].x=read()
#define rdy(i) point[i].y=read()
using namespace std;
int n,m,k;
long long dp[N],inv[N],re[N];
long long mod=1e9+7;
inline int read()
{int ra,fh;char rx;rx=getchar(),ra=0,fh=1;while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();if(rx=='-')fh=-1,rx=getchar();while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh;
}
struct node
{int x,y;
}point[N];
bool cmp(node a,node b){return a.x==b.x?a.y<b.y:a.x<b.x;
}
long long get_inv(long long x)
{long long ans=1;long long y=mod-2;while(y){if(y&1) ans=ans*x%mod;x=x*x%mod;y>>=1;}return ans;
}
void init()
{re[0]=1;int tm=m+n-2;for(int i=1;i<=tm;i++)re[i]=re[i-1]*i%mod;inv[tm]=get_inv(re[tm]);for(int i=tm-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;return ;
}
int main()
{n=read();m=read();k=read();for(int i=1;i<=k;i++) rdx(i),rdy(i);sort(point+1,point+1+k,cmp);point[++k].x=n;point[k].y=m;init();for(int i=1;i<=k;i++){ 	int a=point[i].x+point[i].y-2,b=point[i].x-1;dp[i]=calc(a,b)%mod;for(int j=1;j<i;j++)if(x1>=x2 && y1>=y2){int ce=(x1-x2+y1-y2);int xe=(x1-x2);dp[i]=(dp[i]-dp[j]*calc(ce,xe)%mod+mod)%mod;		} 	}printf("%lld\n",dp[k]);return 0;
}

这篇关于51 nod 1486 大大走格子 容斥原理+组合数学+dp的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/464404

相关文章

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4869(逆元+求组合数)

//输入n,m,n表示翻牌的次数,m表示牌的数目,求经过n次操作后共有几种状态#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#includ

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

uva 10014 Simple calculations(数学推导)

直接按照题意来推导最后的结果就行了。 开始的时候只做到了第一个推导,第二次没有继续下去。 代码: #include<stdio.h>int main(){int T, n, i;double a, aa, sum, temp, ans;scanf("%d", &T);while(T--){scanf("%d", &n);scanf("%lf", &first);scanf

uva 10025 The ? 1 ? 2 ? ... ? n = k problem(数学)

题意是    ?  1  ?  2  ?  ...  ?  n = k 式子中给k,? 处可以填 + 也可以填 - ,问最小满足条件的n。 e.g k = 12  - 1 + 2 + 3 + 4 + 5 + 6 - 7 = 12 with n = 7。 先给证明,令 S(n) = 1 + 2 + 3 + 4 + 5 + .... + n 暴搜n,搜出当 S(n) >=