Atcoder-ABC156-DEF题解

Atcoder题解汇总

ABC 156

D. Bouquet (朴素求组合数,补集思想)

题意
n 种花里挑,每种花只有一个,最后花的数量不能是 a 或者 b,求所有方案数 mod1e9+7

数据范围
2n109
1a<bmin(n,2105)

思路

  • 很容易想到用所有的方案数 - 选 a 个和 b 个的方案数
  • n 太大怎么办? 发现 ab 都在 2e5 范围内,由组合数公式 Cnr=n!r!(nr)!, 可以在 O(a) 时间内求出
  • 预处理阶乘和逆元即可

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 endl "\n" using namespace std; /*----------------------------------------------------------------------------------------------------*/ const int mod = 1e9 + 7; 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; } int main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); ll n, a, b; cin >> n >> a >> b; ll c1 = 1, c2 = 1; for (int i = 1; i <= a; i ++) { c1 = c1 * (n - a + i) % mod * qmi(i, mod - 2, mod) % mod; } for (int i = 1; i <= b; i ++) { c2 = c2 * (n - b + i) % mod * qmi(i, mod - 2, mod) % mod; } cout << (qmi(2, n, mod) - c1 - c2 + 2 * mod - 1 ) % mod << endl; return 0; }

E. Roaming (组合计数,隔板法)

题意

n 个房间,每个房间 1 个人,定义一个事件是某个人在 n 个房间中任意移动一次,但不能待在原地, 询问 k 次事件后,各房间有多少种情况,答案 mod1e9+7

数据范围
3n2105
2k109

思路

  • 首先题目可以构造出所有房间仍然是 1 个人的情况,形成环即可。并且题目并不关心人的编号,只关心各房间情况
  • 先划分子集,按照最后情况中人数为 0 的房间的个数进行划分。
  • 假设有 m 个房间为空, 那么要在 nm 个房间内塞 n 个人且不为空。
  • 问题等价于,对于 n 个小球中间的 n1 个位置放置 nm1 个隔板,形成 nm 个不为空的间隔的方案数。
  • 选择 m 个房间方案数为 Cnm ,放置隔板方案数为 Cn1nm1

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 endl "\n" using namespace std; /*----------------------------------------------------------------------------------------------------*/ const int N = 2e5 + 10, mod = 1e9 + 7; ll fact[N], inv[N], n, k; 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; } ll C (int a, int b) { if (a < b) return 0; return fact[a] * inv[b] % mod * inv[a - b] % mod; } int main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin >> n >> k; ll ans = 0; fact[0] = 1; inv[0] = 1; for (int i = 1; i <= n; i++) { fact[i] = fact[i - 1] * i % mod; inv[i] = qmi(fact[i], mod - 2, mod); } for (int i = 0; i <= min(n - 1, k); i++) { ans = (ans + C(n, i) * C(n - 1, n - i - 1) % mod) % mod; } cout << ans << endl; return 0; }

F. Modularness (补集,用商判断两数模意义下的大小)

题意

给了长度为 k 的数组 d,下标从 0 开始。进行 q 次询问。
每次询问输入 n,x,m, 有长度为 n 的数组 a,下标从 0 开始。
其中:

aj={xj = 0aj1+dj1modm0 < j < n

对于每次询问,输出有多少 j(0<j<n1) 满足 (ajmodm)<(aj+1modm)

数据范围
1k,q5000
0di109
2n109
0x109
2m109

思路

  • 从题目可以看出每个 aid 的环形前缀和,并且对于每次询问将 di%m 并不会影响答案。
  • 对于相邻的 ai,ai+1 其实只差一个 di ,如果对答案有贡献则表明 (ai+di)/m 没有增加。
  • 然而正面计算其实很困难,考虑求补集,即 (ajmodm)>=(aj+1modm)
    • 对于 (ajmodm)=(aj+1modm) 的情况, 只需要关心 di=0 的地方即可
    • 对于 (ajmodm)>(aj+1modm) 的情况,由于每个 di<m ,发生这种情况当且仅当 aj+1/m=aj/m+1
      • 这个题的关键点就是这种局部的累积可以在末尾算出,即数量为 an1/ma0/m=an1mx/m
      • an1 是可以在 O(k) 时间内计算出来的,计算每个 di 的累加次数即可。注意计算 an1 只需要进行 n1 次加法,代码中的一个细节。
  • 时间复杂度 O(qk)

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 endl "\n" using namespace std; /*----------------------------------------------------------------------------------------------------*/ int main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int k, q; cin >> k >> q; vector<ll> d(k, 0); for (int i = 0; i < k; i ++) cin >> d[i]; while (q--) { int n, x, m; cin >> n >> x >> m; vector<ll> cd(k); int cnt = 0; for (int i = 0; i < k; i ++){ cd[i] = d[i] % m; } ll a = x; n--; // 实际进行 n - 1 次加法 for (int i = 0; i < k; i++) { // 计算补集 a += (n / k) * cd[i]; if (i < n % k) a += cd[i]; if (!cd[i]) { cnt += n / k; if (i < n % k) cnt++; } } cnt += a / m - x / m; cout << n - cnt << endl; } return 0; }

__EOF__

本文作者Roshin
本文链接https://www.cnblogs.com/Roshin/p/ABC156.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Roshin  阅读(94)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)
-->
点击右上角即可分享
微信分享提示