首页 > 其他分享 >Codeforces Round 976 (Div. 2) and Divide By Zero 9.0

Codeforces Round 976 (Div. 2) and Divide By Zero 9.0

时间:2024-09-30 16:13:20浏览次数:1  
标签:std 976 Divide int LL Codeforces cin tag operatorname

目录

写在前面

补题地址:https://codeforces.com/gym/104128

上大分失败呃呃呃呃

有点过载了妈的我现在应该去打会儿游戏。

A 签到

等价于求 \(n\) 在 \(k\) 进制下各位之和,写一个类似快速幂的东西。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    LL n, k; std::cin >> n >> k;
    LL ans = 0;
    if (k == 1) {
      ans = n;
    } else {
      LL x = k;
      while (x <= n) x *= k;
      x /= k;
      while (n) {
        if (x <= n) ans += n / x, n %= x;
        x /= k;
      }
    }
    std::cout << ans << "\n";
  }
  return 0;
}

B 数学,模拟

我是打表找的规律哈哈,计算式与下述分析是等价的但是仅使用了乘法避免了开根的精度丢失,也是非常幸运了。

考虑证明。发现每盏灯 \(i\) 被翻转的次数即 \(i\) 的因数个数 \(\operatorname{d}(i)\)。

一个众所周知的结论是对于每个数的因数都是成对儿存在的,即有 \(d | i \iff \frac{i}{d} | i\)。则可知若 \(i\) 为平方数,则 \(\operatorname{d}(i)\) 为奇数,灯将在最后是关闭的,否则 \(\operatorname{d}(i)\) 为偶数,灯在最后是开启的。

于是考虑对于每次询问 \(k\) 二分答案,每次检查 \(\operatorname{mid}\) 中平方数的个数 \(\left\lfloor\sqrt{i}\right\rfloor\),则最小的满足 \(\operatorname{mid} -\left\lfloor\sqrt{i}\right\rfloor = k\) 的 \(\operatorname{mid}\) 即为答案。

呃呃这题数据范围较大仅用 sqrt 开根精度不够,按上述实现需要用 sqrtl 保证返回值为 long double

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    LL k, ans = 1; std::cin >> k;
    for (LL l = 1, r = ceil(sqrt(1.0 * k)); l <= r; ) {
      LL mid = (l + r) / 2ll;
      if (k <= 1ll * mid * (mid + 1)) {
        ans = mid;
        r = mid - 1;
      } else {
        l = mid + 1;
      }
    }
    std::cout << k + ans << "\n";
  }
  return 0;
}

C 二进制,拆位

第一眼看到这个式子感觉非常不可做的样子,万一相减时出现借位怎么办?进一步观察发现是不会出现的。考虑位运算的性质,可知 \(a\) 中为 1 的各位在 \(a \operatorname{or} b\) 中一定存在,\(a \operatorname{and} c\) 中为 1 的各位在 \(a\) 中一定存在,则可以保证 \(a \operatorname{or} b - a \operatorname{and} c\) 一定不会出现借位的情况,于是可以拆位考虑。

然后手玩下发现,对于第 \(i\) 位:

  • 若 \(b, c\) 均为 1,则 \(a\) 中该位必须与 \(d\) 中该位相反,才可令该位合法;
  • 若 \(b, c\) 均为 0,则 \(a\) 中该位必须与 \(d\) 中该位相同,才可令该位合法;
  • 否则无论 \(a\) 取何值都不会影响该位的计算结果,可直接判断该位是否合法;

一个更进一步的做法,是发现上述前两种情况相当于 \(a = b\oplus c\),于是直接检查 \(a=b\oplus c\) 是否合法即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
LL a, b, c, d;
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> b >> c >> d;
    a = 0;
    for (LL i = 0; i < 60; ++ i) {
      LL j = (1ll << i);
      if ((b >> i & 1) && (c >> i & 1)) {
        if (!(d >> i & 1)) a |= j;
      } else if (b >> i & 1) {
        continue;
      } else if (c >> i & 1) {
        continue;
      } else {
        if (d >> i & 1) a |= j;
      }
    }
    if ((a | b) - (a & c) != d) a = -1;
    std::cout << a << "\n";
  }
  return 0;
}

D 暴力,并查集

呃呃赛时写了一坨不知所谓的东西第一次 WA26 然后把关键部分复制了两遍就过了哈哈最后也没 fst。

在题面里就特别指出 \(d_i\le 10\),这个性质一看就非常重要。

考察连通块的数量,且点的数量不太多,考虑使用并查集维护。此时发现对于一次操作所有点两两连边是并无必要的,仅需考察每次操作中每个点与下一个点连边即可。

又 \(d_i\le 10\),则对于每个点仅会向之后连至多十条边,于是考虑直接维护每个点是否向 \(i+j(1\le j\le 10)\) 连边,再并查集维护连通性即可。

可以通过类似差分的方式维护每个点是否被修改。记 \(\operatorname{tag}_{i, j}(1\le j\le 10)\) 表示节点 \(i\) 需要向节点 \(i+j\) 连边的条数,修改时仅需令 \(\operatorname{tag}_{a, d}:=\operatorname{tag}_{a, d} + 1, \operatorname{tag}_{a + kd, d} :=\operatorname{tag}_{a + kd, d} -1\),然后在枚举枚举每个点及连边时更新下一个点的连边数即可。

总时间复杂度 \(O(n)\) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, m, fa[kN];
int tag[kN][11];
//=============================================================
int find(int x_) {
  return fa[x_] == x_ ? x_ : fa[x_] = find(fa[x_]);
}
void merge(int x_, int y_) {
  int fx = find(x_), fy = find(y_);
  if (fx == fy) return;
  fa[fx] = fy;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n >> m;
    for (int i = 1; i <= n; ++ i) {
      for (int j = 1; j <= 10; ++ j) tag[i][j] = 0;
      fa[i] = i; 
    }
    for (int i = 1; i <= m; ++ i) {
      int a, d, k; std::cin >> a >> d >> k;
      tag[a][d] = tag[a][d] + 1;
      tag[a + k * d][d] = tag[a + k * d][d] - 1;
    }

    int ans = 0;
    for (int i = 1; i <= n; ++ i) {
      for (int j = 1; j <= 10; ++ j) {
        if (tag[i][j] > 0) {
          merge(i, i + j);
          tag[i + j][j] = tag[i + j][j] + tag[i][j];
        }
      }
    }
    
    for (int i = 1; i <= n; ++ i) if (find(i) == i) ++ ans;
    std::cout << ans << "\n";
  }
  return 0;
}
/*
1
100 4
1 1 1
1 1 1
1 1 1
2 1 1
100 1 0

100 1
19 2 4
*/

E 概率DP

妈的这题 std 明明是 \(O(n\log^2 v)\) 的写那么麻烦但为什么会把 \(O(nv)\) 的暴力放过去?出题人犯病了?

首先有一个非常简单的暴力 DP 并且可以过这题呃呃,考虑记 \(f_{i, j}\) 表示随机选择 \(a_1\sim a_i\),异或和为 \(j\) 的概率,初始化 \(f_{0, 0} = 1\),则有显然的类似背包的转移:

\[\begin{cases} f_{i, j}\leftarrow \frac{p_1}{10^4}\times f_{i - 1, j}\\ f_{i, j \oplus a_i} \leftarrow \left(1-\frac{p_1}{10^4}\right)\times f_{i - 1, j} \end{cases}\]

答案即为:

\[\sum_{i=0}^{1023} f_{n, i}\times i^2 \]

时间复杂度 \(O(nv)\) 级别,可过但是太傻比了显然是出题人出题唐氏了这都没想到就嗯造数据给放过去了。

\(O(nv)\) 暴力 DP:

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
const LL p = 1e9 + 7;
//=============================================================
int n, a[kN], prob[kN];
LL f[2][1024];
//=============================================================
LL qpow(LL x_, LL y_) {
  LL ret = 1;
  while (y_) {
    if (y_ & 1) ret = ret * x_ % p;
    x_ = x_ * x_ % p, y_ >>= 1ll;
  }
  return ret;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  LL inv = qpow(10000, p - 2);

  int T; std::cin >> T;
  while (T --) {
    std::cin >> n;
    for (int i = 1; i <= n; ++ i) std::cin >> a[i];
    for (int i = 1; i <= n; ++ i) std::cin >> prob[i];

    int now = 1;
    for (int i = 0; i <= 1023; ++ i) f[0][i] = 0;
    f[0][0] = 1;
    for (int i = 1; i <= n; ++ i) {
      LL p1 = prob[i] * inv % p, p2 = (1 - p1 + p) % p;
      for (int j = 0; j <= 1023; ++ j) f[now][j] = p2 * f[now ^ 1][j] % p;
      for (int j = 0; j <= 1023; ++ j) f[now][j ^ a[i]] += p1 * f[now ^ 1][j] % p;
      for (int j = 0; j <= 1023; ++ j) f[now][j] %= p;
      now ^= 1;
    }
    LL ans = 0;
    for (int i = 0; i <= 1023; ++ i) ans += f[now ^ 1][i] * i * i % p, ans %= p;
    std::cout << ans << "\n";
  }
  return 0;
}
/*
1
1
1
10000
*/

F

写在最后

学到了什么:

  • B:打表好用。
  • C:经典拆位考虑。

标签:std,976,Divide,int,LL,Codeforces,cin,tag,operatorname
From: https://www.cnblogs.com/luckyblock/p/18442029

相关文章

  • Codeforces Round 975 (Div. 2)
    C.CardsPartition(C)对于每个确定的size,有便捷的方法判断该值是否合法:组数最小为\(a_{max}\),若\(k\)张牌可以填出\(a_{max}\)组牌堆则合法;将余下的牌当成新的一组,若\(k\)张牌可以凑出一整组则合法;其余情况不合法。由于check过程为\(O(1)\),可从大到小暴力枚举size,时间......
  • Codeforces Round 976 (Div. 2)
    传送门A.输出\(n\)在\(k\)进制下各数位之和B.一个位置被反转当且仅当有奇数个因子,有奇数个因子的数一定是完全平方数。二分\(n\)即可C.枚举二进制下每一位,发现\(b,c,d\)再这一位最多有八种可能,对于每一种情况都能得到在这一位\(a\)的值,分类讨论就行了。D.E.\(......
  • Codeforces Round 976 (Div. 2)
    B:很容易发现只有因数个数为偶数的灯泡是亮的。所以只有完全平方数的因数是奇数个。实现上可以二分。但是sqrt是double的必须开sqrtl才是longdouble的,才能满足这题longlong的数据范围。人给我卡傻了。哈哈。#include<bits/stdc++.h>usingnamespacestd;#defineintunsign......
  • Codeforces Round 975 (Div. 2) CE
    来源:CodeforcesRound975(Div.2)C.CardsPartition题意本身有\(a_i\)张\(i\)牌,现在将牌分成若干份,每一份牌数相等,且每一份都由不同的牌组成同时有\(k\)次补充任意牌的机会,求最多分成一份几张牌思路假定分成\(m\)份,每份\(p\)张牌,那么所有牌加补充的牌等于\(m*p......
  • Codeforces Round 975 (Div. 2) A~F 题解(Div.1 A~D)
    CodeforcesRound975(Div.2)A~F题解(Div.1A~D)也是补完整场了。A.MaxPlusSize枚举最大值的位置,使长度最长,更新答案。B.AllPairsSegments每个线段内部的点的答案都是一样的,在处理一下线段两边边界的点。被包含次数就是左边\(\times\)右边。用\(map\)记录答案......
  • Codeforces Round 975 (Div. 2)
    目录写在前面A签到B排序,模拟C数学,贪心D模拟,思维E树,贪心,暴力or长链剖分F贪心,枚举写在最后写在前面比赛地址:https://codeforces.com/contest/2019。唉唉不敢打div1只敢开小号打div2太菜了。A签到显然最优方案只有两种——取所有奇数位置或所有偶数位置。///*By......
  • Codeforces Round 975 (Div. 2) A-E
    人生中第一次正常\(div2\)爆写5题。cf给我正反馈最大的一次A直接找到最大的数字的位置,然后判断一下这个数字的位置下标的奇偶性就好了。然后如果有多个最大的就取奇数位置的。这样可以算出来一定是最优解#include<bits/stdc++.h>#definelllonglongusingnamespacestd;inl......
  • Codeforces Round 975 Div.2 C题 解析
    C题题目链接:Problem-C-Codeforces题目描述思路......
  • Codeforces Round 975 (Div. 2) 题解:D、E
    第一次进前50,上分最爽的一次D.Speedbreaker对\(a\)按照时间升序排序,不难发现,对于升序排序后的数组,当我们搜到时间\(t\)时,记录已经搜过的时间对应原城市编号最小值为\(l\),最大值为\(r\),则我们一定要在\(a_t\)时间之前走过所有\([l,r]\)之间的城市。则若\(r-l+1>a_t\)则无解,因......
  • Codeforces Round 944 (Div. 4) A~G题解
    A\(min\)函数和\(max\)函数的使用,按照格式输出即可。#include<bits/stdc++.h>usingnamespacestd;typedeflonglongLL;typedefpair<int,int>PII;voidsolve(){intx,y;cin>>x>>y;cout<<min(x,y)<<'......