(水博客预警
关于补了题才发现这三道题真的很有意思这件事
A就是一个单纯的暴力枚举
B是由于数据范围过大而不得不进行的推公式(涉及到两个区间求交集的运算技巧和公式)
C是考察对于B的理解和灵活运用程度(顺便考察了取模运用)
2023牛客寒假算法基础集训营2_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)
题目是看了题解才明白的,并为这有趣的公式和思路感到震惊
对于B题,给出了两个范围,要求满足从两个范围中各取一数a, b使得a + b = n,只需判断a或b的取值范围(这里我们用a的范围为例),答案从两个范围取大值即可。
首先,a本身是a ∈ [L1, R1],又n - a = b,则n - a ∈ [L2, R2] ==> a ∈ [n - R2, n - L2] ,显然,a的取值便是[L1, R1] ∩ [n - R2, n - L2]。
而求交集的代码公式也很好理解
x ∈ [max(L1, n - R2), min(R1, n - L2)] (后边界取小,前边界取大)
同理推出求并集公式
x ∈ [min(L1, n - R2), max(R1, n - L2)] (后边界取大,前边界取小)
(这是不是就算把数学和代码联系到一起辣!
此外还要注意如果R1和R2之和小于n和L1和L2之和大于n的情况是取不到结果的,直接就是0了(也可以if特判答案小于等于0的情况都输出0)
AB已ac代码:
#include<iostream>
#include<algorithm>
#include<math.h>
#include<map>
#define ll long long
#define int ll
using namespace std;
const int maxn = 1e6 + 10;
signed main()
{
ios::sync_with_stdio(false);
cin.tie();
cout.tie();
int t;
cin>>t;
while (t--) {
int n;
cin>>n;
int l1, l2, r1, r2;
cin>>l1>>r1>>l2>>r2;
if (l1 + l2 > n || r1 + r2 < n) {
cout<<"0\n";
continue;
}
cout<<max(min(r2, n - l1) - max(l2, n - r1) + 1, min(r1, n - l2) - max(l1, n - r2) + 1)<<"\n";
}
return 0;
}
对于C题,已经给出了许多个区间的情况下,任选两个不同的区间进行前边的计算,就是用全部的可能性减去同一个区间自身取的情况,全部可能性是枚举n范围内的数字i,用他所出现过的区间个数 * (n - i) 出现过的区间个数;自身取就是B题的简化,两个范围都是L到R,套用公式舍去小于0或2 * L > n和2 * R < n的情况(一个意思);外加取模运算和减法取模的特性即可求解。
C题已ac代码:
#include<bits/stdc++.h>
#define ll long long
#define int ll
using namespace std;
const int mod = 998244353;
const int maxn = 1e6 + 10;
int box[maxn];
signed main()
{
int n, m;
cin>>n>>m;
int sum1 = 0;
while (m--) {
int l, r;
cin>>l>>r;
box[l]++;
box[r + 1]--;
if (2 * l > n || 2 * r < n || n - l < 0) {
continue;
}
// if (min(r, n - l) - max(l, n - r) + 1 <= 0) {
// continue;
// }
sum1 = sum1 % mod + (min(r, n - l) - max(l, n - r) + 1) % mod;
}
for (int i = 1; i <= maxn; i++) {
box[i] = (box[i - 1] + box[i]) % mod;
}
int sum2 = 0;
for (int i = 1; i < n; i++) {
sum2 = sum2 % mod + box[i] % mod * (box[n - i] % mod) % mod;
}
cout<<(sum2 % mod - sum1 % mod + mod) % mod;
return 0;
}
by yq
ps:水博客+记录公式和想题思路,扩展对于有关贡献值的题目的考虑方式