solutionof ARC112 A,B

Solution of ARC 112

A. A - B = C

statement

求满足 A − B = C A-B=C AB=C L ≤ A , B , C ≤ R L\leq A,B,C\leq R LA,B,CR 三元组 ( 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(LAR) B ( L ≤ B ≤ R ) B(L\leq B \leq R) B(LBR) A − B A-B AB 必然不可能超过 R R R (即,大于有边界)。因此,只需要满足 A − B ≥ L A-B\geq L ABL 即可。

然后我们可以从加法的角度考虑。令 A = L + x A=L+x A=L+x ,那么就有 A − x = L A-x=L Ax=L 。则对于任意的 x ′ ≤ x x'\leq x xx ,都有 A − x ′ ≥ C A-x'\geq C AxC 。则只需要满足 x ≥ L x\geq L xL 即可。也就是说, x x x 的取值范围为 [ L , R ] [L,R] [L,R] 之间的所有自然数。同时也要满足 x + L ≤ R x+L\leq R x+LR

那么暴力就直接枚举并统计答案即可。

优化也就是,对于一个合法的 x x x ,答案应加上 x − L + 1 x-L+1 xL+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=1RLL+1i=2(1+RLL+1)(RLL+1)=2(R2L+2)(R2L+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 ,可以进行若干次操作,每次操作可以从以下二者中任选一件:

  1. B = − B B=-B B=B ,代价为 1 1 1
  2. B = B − 1 B=B-1 B=B1 ,代价为 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 Bn ;同理,最后执行第一种操作,最小值为 − ( B − n ) = − B + n -(B-n)=-B+n (Bn)=B+n

注意到第二种操作每次可以使 B B B 少一,于是易知,当前情况下, [ − B − n , − B + n ] [-B-n,-B+n] [Bn,B+n] 中的所有值都可以取到。

C C C 为偶数

这时,我们可以不去进行第一种操作。则此时可以取到的最小值为 B − n B-n Bn

若进行第一种操作,那么必须进行 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 n1 次第二种操作。结合上面关于 C C C 为奇数时的讨论,易得此时取到的最小值为 B − n + 1 B-n+1 Bn+1 ,最大值为 B + n − 1 B+n-1 B+n1

(关于这里最大值如何得来:开始用一次操作 1 1 1 ,变为 − B -B B,然后执行操作 2 2 2 ,变为 − B − n + 1 -B-n+1 Bn+1 ,最后再用一次操作 1 1 1 ,变为 B + n − 1 B+n-1 B+n1)。

结合不进行第一种操作的情况,得出 C C C 为偶数时的答案区间就是 [ B − n , B + n − 1 ] [B-n,B+n-1] [Bn,B+n1]


回到原来的问题,注意到一个很棒的性质: C = x C=x C=x 时的答案就已经包含了 C = x − 2 C=x-2 C=x2 时的答案 (显然, x − 2 x-2 x2 元能凑出的数一定可以用 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=C1 时的答案区间,两个区间长度相加再减去她们的并集大小就是答案(这里其实就是一个容斥原理)。


写代码时,请注意 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值