C题:Iroha's Obsession
Problem
给定数字N,和K个单个数字,求出一个数字且满足以下要求
1.大于等于N
2.数字不由给定的K个数字组成
3.该数字尽可能最小
Soution
注意到数据范围较小,考虑对大于等于N的数子进行从小到大的枚举,用bool[]数组将K个数字标记,若枚举到的数字的数位是K个数字的其中之一,则继续枚举,否则,第一个枚举到的数字就一定是最小的
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 10;
bool st[10];
bool sv(int x)
{
while (x)
{
if (st[x % 10])
return false;
x /= 10;
}
return true;
}
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int m, n; cin >> m >> n;
for (int i = 1; i <= n; i++)
{
int x; cin >> x;
st[x] = true;
}
for (int i = m;; i++)
{
if (sv(i))
{
cout << i << endl;
break;
}
}
return 0;
}
D题:Iroha and a Grid
Problem
给定一个NxM的方格图,由起点(1,1)开始走,每次只能向下或者向右走,同时限制:不能走左下角A行xB列的矩形方格,问:走到(N,M)的方案数
Solution
首先看到题,想到的肯定是DP,但数据范围过大,显然是用不了的,但我们也将dp转移式列出来
dp[n][m]=dp[n-1][m]+dp[n][m-1]
更具体的:dp[n][m]=dp[n-1][m]*1+dp[n][m-1]*1
为什么是这样的?
这里需要先知道:分步乘法和分类加法原理
分步乘法,意思是若每个步骤有多种走法Ki,则完成这件事一共有K1*K2*K3.....Kn种走法
若一件事有多种完成方式
分类加法,意思是每种完成方式有Ki种走法,则完成这件事一共有K1+K2+K3......+Kn种走法
对于点(n,m)来说,“到达(n,m)"这件事有从(n,m-1)(n,m)或者(n-1,m)➡(n,m)两种完成方式
对于点(n,m-1),其只有一种方式可以到达(n,m),即向下走
由分步乘法原理可知:由(n,m-1)到达(n,m)的走法有dp[n][m-1]*1种
对于点(n-1,m),其也只有一种方式可以到达(n,m),即向右走
由分步乘法原理可知:由(n-1,m)到达(n,m)的走法有dp[n-1][m]*1种
再由分类加法将其相加,即得出该式子的由来
对于点(n,m)来说,若没有左下角的阻碍,即从左上角到右下角,必然走(n-1)步向下
(m-1)步向右,由组合数定理可得到达右下角的方法有种,由分步乘法可知,这是不关心中间过程的表达式,即:我不关心你中间怎么走的,反正中间的(m-1+n-1)步,必然是存在(m-1)步向右走。
假如我们限定一个点(x,y),问经过该点且到达(n,m)的路径有几条,
先将到达(x,y)的路径条数求出来:,再将(x,y)到(n,m)的路径条数求出来
,此时由分步乘法原理可得:经过该点且到达(n,m)的路径有
条,但显然,有很多路径可以不经过点(x,y)且无法通过讨论的方法将剩余所有路径不重不漏的直接计算出来,重点来了!但此题却不同
左下角的限制区域使得右边出现了一个”口子“即红色区域,该区域的点的方案数由于在限制区域的上方,使得其方案数可以正常求得,且每个到达右下角的点必然经过三个点中的一个(可能会有多个,此时为了避免重复计算,我们在算出到达红色区域的方块的个数时,不乘以红色方块到达右下角的方案数,而是乘以红色方块各自下面的方块到达右下角的方案数,这样相当于使得红色方块不向右走,而是只往向下走(因为向右走的方案已经包含在右边方块的方案数里面了)),这使得我们只要将这三个点作为中间点进行计算,便可以不重不漏的计算出所有方案
由于模数为质数且N<=100000,所以可用求逆元的方式求组合数,有多个数据,所以提前算出阶乘和阶乘的逆元即可
Code
const int N = 2e5 + 10;
int jc[N], jcny[N];
int ksm(int a, int b)
{
int ans = 1;
while (b)
{
if (b & 1)ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int C(int a, int b)
{
return jc[a] * jcny[b]%mod* jcny[a - b] % mod;
}
void iint()
{
jc[0] = jcny[0] = 1;
for (int i = 1; i <= 2e5+10; i++)jc[i] = jc[i - 1] * i % mod;
jcny[200005] = ksm(jc[200005], mod - 2);
for (int i = 200004; i >= 1; i--)jcny[i] = jcny[i + 1] * (i+1) % mod;
}
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int n, m, a, b; cin >> n>> m >> a >> b;
iint();
int ans = 0;
for (int i = b + 1; i <= m; i++)
{
ans += C(n - a + i-2, i-1 ) * C(a - 1 + m - i, m - i);
ans %= mod;
}
cout << ans << endl;
return 0;
}