文章目录
Solution of ARC 112
A. A - B = C
statement
求满足 A − B = C A-B=C A−B=C 且 L ≤ A , B , C ≤ R L\leq A,B,C\leq R L≤A,B,C≤R 三元组 ( A , B , C ) (A,B,C) (A,B,C) 的数量。请注意, ( 5 , 2 , 3 ) (5,2,3) (5,2,3) 和 ( 5 , 3 , 2 ) (5,3,2) (5,3,2) 是不同的。 A , B , C , L , R A,B,C,L,R A,B,C,L,R 均为自然数。
solution
不难发现,对于任意一个 A ( L ≤ A ≤ R ) A(L\leq A\leq R) A(L≤A≤R) 和 B ( L ≤ B ≤ R ) B(L\leq B \leq R) B(L≤B≤R), A − B A-B A−B 必然不可能超过 R R R (即,大于有边界)。因此,只需要满足 A − B ≥ L A-B\geq L A−B≥L 即可。
然后我们可以从加法的角度考虑。令 A = L + x A=L+x A=L+x ,那么就有 A − x = L A-x=L A−x=L 。则对于任意的 x ′ ≤ x x'\leq x x′≤x ,都有 A − x ′ ≥ C A-x'\geq C A−x′≥C 。则只需要满足 x ≥ L x\geq L x≥L 即可。也就是说, x x x 的取值范围为 [ L , R ] [L,R] [L,R] 之间的所有自然数。同时也要满足 x + L ≤ R x+L\leq R x+L≤R 。
那么暴力就直接枚举并统计答案即可。
优化也就是,对于一个合法的
x
x
x ,答案应加上
x
−
L
+
1
x-L+1
x−L+1 。这个东西显然可以用等差数列求和公式乱搞一下:
ans
=
∑
i
=
1
R
−
L
−
L
+
1
i
=
(
1
+
R
−
L
−
L
+
1
)
(
R
−
L
−
L
+
1
)
2
=
(
R
−
2
L
+
2
)
(
R
−
2
L
+
1
)
2
\text {ans}=\sum_{i=1}^{R-L-L+1} i=\frac{(1+R-L-L+1)(R-L-L+1)}{2}=\frac{(R-2L+2)(R-2L+1)}{2}
ans=i=1∑R−L−L+1i=2(1+R−L−L+1)(R−L−L+1)=2(R−2L+2)(R−2L+1)
O
(
1
)
\mathcal O(1)
O(1) 计算即可。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int T;
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x * f;
}
int main()
{
T = read();
while (T--)
{
ll L, R; L = read(), R = read();
ll x = L; ll ans = 0, ansq = 0;
// while (L + x <= R) {ansq += (ll)(x - L + 1); ++x; }
// cout << ansq << "\n";
if (L == R && L && R) {puts("0"); continue;}
if (R - L < L) {puts("0"); continue;}
ans = (ll)(R - 2*L + 2)*(ll)(R- 2*L + 1) / 2;
cout << ans << "\n";
}
return 0;
}
B. B - – - B
statement
给定整数 B , C B,C B,C ,可以进行若干次操作,每次操作可以从以下二者中任选一件:
- 令 B = − B B=-B B=−B ,代价为 1 1 1 ;
- 令 B = B − 1 B=B-1 B=B−1 ,代价为 2 2 2 。
问,在代价不超过 C C C 的前提下,做多可以获得多少个不同的 B B B (初始的也算) 。
solution
首先考虑的是,用恰好
C
C
C 元的结果。
注意到两种操作的代价一奇一偶,这启示我们将
C
C
C 按照其奇偶性分类讨论。
C C C 为奇数
注意到,第一种操作执行多次是反复横跳,不会对答案产生贡献。而 C C C 为奇数,所以必须执行一次第一次操作。
为了便于理解,首先看能够对答案产生贡献的第二种操作。显然的,其一共可以执行 n = ⌊ C 2 ⌋ n=\left\lfloor\frac{C}{2}\right\rfloor n=⌊2C⌋ 次,然后还剩一次执行第一种操作。若先执行第一种操作,则有可以取到的最小值为 − B − n -B-n −B−n ;同理,最后执行第一种操作,最小值为 − ( B − n ) = − B + n -(B-n)=-B+n −(B−n)=−B+n 。
注意到第二种操作每次可以使 B B B 少一,于是易知,当前情况下, [ − B − n , − B + n ] [-B-n,-B+n] [−B−n,−B+n] 中的所有值都可以取到。
C C C 为偶数
这时,我们可以不去进行第一种操作。则此时可以取到的最小值为 B − n B-n B−n 。
若进行第一种操作,那么必须进行 2 2 2 次(否则无法凑出代价 C C C) ,那么就相当于少了 2 2 2 代价,根据 n = ⌊ C 2 ⌋ n=\left\lfloor\frac{C}{2}\right\rfloor n=⌊2C⌋ ,易知此时只能执行 n − 1 n-1 n−1 次第二种操作。结合上面关于 C C C 为奇数时的讨论,易得此时取到的最小值为 B − n + 1 B-n+1 B−n+1 ,最大值为 B + n − 1 B+n-1 B+n−1。
(关于这里最大值如何得来:开始用一次操作 1 1 1 ,变为 − B -B −B,然后执行操作 2 2 2 ,变为 − B − n + 1 -B-n+1 −B−n+1 ,最后再用一次操作 1 1 1 ,变为 B + n − 1 B+n-1 B+n−1)。
结合不进行第一种操作的情况,得出 C C C 为偶数时的答案区间就是 [ B − n , B + n − 1 ] [B-n,B+n-1] [B−n,B+n−1] 。
回到原来的问题,注意到一个很棒的性质: C = x C=x C=x 时的答案就已经包含了 C = x − 2 C=x-2 C=x−2 时的答案 (显然, x − 2 x-2 x−2 元能凑出的数一定可以用 x x x 元凑出),因此令 [ a , b ] [a,b] [a,b] 为 C = C C=C C=C 时的答案区间, [ c , d ] [c,d] [c,d] 为 C = C − 1 C=C-1 C=C−1 时的答案区间,两个区间长度相加再减去她们的并集大小就是答案(这里其实就是一个容斥原理)。
写代码时,请注意 C = 0 C=0 C=0 ,两个答案区间不相交等细节的处理。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll B, C;
void calc(ll C, ll& x, ll& y)
{
if (!C) {x = B, y = B;} // C = 0 ,只能为 B
else if (C & 1) {ll n = C / 2; x = -B - n; y = -B + n;}
else {ll n = C / 2; x = B - n; y = B + n - 1;}
return ;
}
int main()
{
cin >> B >> C;
ll a, b, c, d;
calc(C, a, b);
calc(C - 1, c, d);
cout << (b-a + 1) + (d-c + 1) - max(0ll, min(b, d) - max(a, c) + 1) << "\n";
// 并集可能不存在,所有要和 0ll 比较一下
return 0;
}