Atcoder-ABC154-EF 题解

Atcoder题解汇总

ABC 154

E. Almost Everywhere Zero (简单数位DP)

题意

\(1~n\) 中, 非零数位恰好有 \(k\) 个的数字个数

数据范围
\(1\leq n \leq 10^{100}\)

思路

  • 一眼数位DP, 常规状态记录, \(dp[i][j]\) 搜到了第 \(i\) 位, 非零个数位 \(j\) 的数字个数

Solution

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef double db;
#define arr(x) (x).begin(),(x).end()
#define x first
#define y second
#define pb push_back
#define mkp make_pair
#define endl "\n"
using namespace std;
const int N = 110;
ll dp[N][4], a[N];
int k;

ll dfs(int pos, int cnt, bool lead, bool limit){
	if(cnt > k) return 0;
	if(pos == -1) return cnt == k;	// 最后判断符合条件需要严格 k 个数
	if(!limit && !lead && dp[pos][cnt] != -1) return dp[pos][cnt];
	int up = limit ? a[pos] : 9;
	if(cnt == k) up = 0;
	ll res = 0;
	for(int i = 0; i <= up; i++){
		int t = (i != 0);
		res += dfs(pos - 1, cnt + t, lead && i == 0, limit && i == a[pos]);
	}
	if(!limit && !lead) dp[pos][cnt] = res;
	return res;
}

int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	string s;
	cin >> s >> k;
	int pos = 0;
	memset(dp, -1, sizeof dp);
	for(int i = s.size() - 1; ~i; i--)
		a[pos++] = s[i] - '0';
	cout << dfs(pos - 1, 0, true, true) << endl;
    return 0;
}

F. Many Many Paths (组合数)

题意

二维平面, 输入两个点的坐标 \((r1, c1), (r2, c2)\), 求从 \((0,0)\) 到两点之间矩形区域内的点的路径数总和, 设 \(f(i,j)\) 为到 (i,j) 路径数, 则求 \(\Sigma_{r_1\leq i\leq r_2,c_1\leq j\leq c_2} f(i,j)\) 大小

数据范围
\(1\leq r1\leq r2\leq 10^6\)
\(1\leq c1\leq c2\leq 10^6\)

思路

  • 用二维前缀和容斥的思想, 如果我们知道 \(g(r,c)\) 代表 \((0,0)\)\((r,c)\) 的方案和, 则答案等于 \(g(r_2,c_2)-g(r_1-1,c_2)-g(r2, c_1-1) + g(r_1-1,c_1-1)\)
  • 明显可知 \(f(i,j)=C_{i+j}^i\), 则需要求 \(\Sigma_{i=r_1}^{r_2} \Sigma_{j=c_1}^{c_2} C_{i+j}^j\), 考虑如何求单个 \(g(r,c)\) 就可以解决此题
  • 即便预处理逆元和阶乘, 也需要 \(O(n^2)\) 的复杂度, 考虑优化计算
  • 有公式 \(C_{m+r+1}^r = \Sigma_{i=0}^r C_{m+i}^i\) , 在此题意义便是 \(f(r,0)+f(r,1)+...+f(r,c)=f(r+1,c)\)
  • 因此内层循环被优化掉, 具体推导可以见代码注释部分, \(O(n)\) 求解各个 \(g(r,c)\)

Solution

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef double db;
#define arr(x) (x).begin(),(x).end()
#define x first
#define y second
#define pb push_back
#define mkp make_pair
#define endl "\n"
using namespace std;
const int mod = 1e9 + 7;
const int N = 2e6;
ll fact[N + 10], f1[N], f2[N], g1[N], g2[N];
ll inv[N + 10];

ll qmi(ll a, ll k, int mod) {
	ll res = 1;
	while (k) {
			if (k & 1)
					res = res * a % mod;
			a = a * a % mod;
			k >>= 1;
	}
	return res;
}

// ans = g(r2, c2) - g(r2, c1 - 1) - g(r1 - 1, c2) + g(r1 - 1, c1 - 1);	
// g(r, c) = f(0,0) +...+ f(0, c) + ... + f(r,0) + ... + f(r, c);
// g(r, c) = f(1,c) + f(2, c) + f(3,c) +...+ f(r+1,c)
// f(r,c) = (r+c)! * inv[r] * inv[c];

ll C(int a, int b) {
	return fact[a] * inv[b] % mod * inv[a - b] % mod;
}

int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int r1, r2, c1, c2;
	cin >> r1 >> c1 >> r2 >> c2;
	fact[0] = inv[0] = 1;
	for (int i = 1; i <= N + 5; i++) {
		fact[i] = 1ll * fact[i - 1] * i % mod;
		inv[i] = inv[i - 1] * qmi(i, mod - 2, mod) % mod;
	}
	ll g1 = 1, g2 = 1, g3 = 1, g4 = 1;
	for (int i = 1; i <= r2 + 1; i++) {
		g1 = (g1 + C(i + c2, c2)) % mod;
		g2 = (g2 + C(i + c1 - 1, c1 - 1)) % mod;
	}
	for (int i = 1; i <= r1; i++) {
		g3 = (g3 + C(i + c2, c2)) % mod;
		g4 = (g4 + C(i + c1 - 1, c1 - 1)) % mod;
	}
	cout << (g1 - g2 - g3 + g4 + mod) % mod << endl;
    return 0;
}
posted @ 2022-06-10 15:35  Roshin  阅读(51)  评论(0编辑  收藏  举报
-->