当前位置: 首页 > news >正文

【CF】Day56——Codeforces Round 940 (Div. 2) and CodeCraft-23 BCD

B. A BIT of a Construction

题目:

思路:

简单思维题

首先我们肯定可以想到,答案的值不可能超过 k 的最高有效位,比如 1011,答案最多只能是 4 位,那我们考虑时候可以构造出一种方法使得刚好能填满呢?

我们考虑到,如果一个数他是 1111 这种形式,那么可以直接放这个数即可,但是如果是 1000呢?首先我们最优的情况肯定是将后面的 0 全变为 1,且这一定是最优的,因为如果我们可以通过舍弃最高位而获得后面的所有位,比如 1000 我们可以使用 0111 0001 两个构造出,此时答案是 3,可以看出这显然是最优的

那观察归纳发现,其实我们可以先获取 k 的最高有效位 w,然后构造 2^{w} - 1 和 k - (2^{k} - 1)这两个数,我们发现这显然是最优的,因为我们最坏情况下都能获得 w - 1的答案,而什么时候能获得 w 呢?显然就是全为 1 的情况了,而这种情况我们用上诉方法也能构造出来

因此只要 n >= 2,我们就能按照上诉方法先构造两个数,然后其余全是 0,否则我们直接输出 k 即可 

代码:

#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"void solve()
{int n, k;cin >> n >> k;if (n == 1){cout << k << endl;return;}int x = 0;for (int i = 31; i >= 0; i--){if (k & (1ll << i)){x = i;break;}}cout << (1ll << x) - 1 << " " << k - (1ll << x) + 1 << " ";for (int i = 1; i <= n - 2; i++) {cout << "0 ";}cout << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}

C. How Does the Rook Move?

题目:

思路:

DP思维题

我们观察发现,一开始的选法其实不重要,重要的是选完后我们还剩下多少个 行/列 可以选择

比如行列是 3 的情况,我们发现我们可以选择删除删除左上角或者左下角的元素,这样只删除了一行/列,这样我们的问题就变为了变成 行列是2的情况,否则我们就会删除 2 行/列,这样就变成了 行/列是 1 的情况

我们发现这其实可以转化为子问题,因此我们可以用递推来写

我们定义 dp[i] 为行/列为 i 时的所有可能,初始化 dp[0] = dp[1] = 1,接下来我们推导转移方程,我们对于 i 行/列 的情况,我们可以只删去一 行/列 看看它能变成什么情况,如果我们放在对角线,那么就变成了 i-1 行/列 的情况,否则我们肯定会删去两 行/列,因为电脑也会下,所以就变成了 i-2 行/列 的情况,因此可以推出 dp[i] = dp[i-1] + dp[i - 2] * (n - 1),但是由于我们可以和电脑交换位置,所以最后应该是 dp[i] = dp[i-1] + dp[i - 2] * (n - 1) * 2

为什么是这样考虑呢?我们假设我们第一个删除的是第一行,那么我们可以放第一个点,即对角线上,或者放后面的点,即其余 (n - 1) 个位置上,我们发现无论我们选哪一行当作开始,我们都会再别的行当作开始的情况下存在(情况之间可以通过平移互换得到),即与我们选哪里当作开始点无关,因此就是 dp[i-1] 而不是 dp[i - 1] * n

最后按照转移方程初始化即可,每次查询获取剩余行数 t,然后直接输出 dp[t] 即可

代码:

#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"
const int MOD = 1e9 + 7;
int dp[300005];
void solve()
{int n, k;cin >> n >> k;int t = n;for (int i = 0; i < k; i++){int r, c;cin >> r >> c;if (r != c){t--;}t--;}cout << dp[t] % MOD << endl;
}signed main()
{dp[0] = dp[1] = 1;for (int i = 2; i <= 300000; i++){dp[i] = dp[i-1] + (2 * i - 2) * dp[i - 2];dp[i] %= MOD;}cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}

D. A BIT of an Inequality

题目:

思路:

好题,值得学习

看到题目给的这个式子,我们不难发现其实就是 f(x,z) ^ y > f(x,z),那么如果要让 f(x,z) 异或 y 后 值变大 那么一定是 y 的最高有效位 w 造成了奉献,即 f(x,z) 的第 w 位是 0

为什么一定是最高有效位造成了奉献呢?假如不是最高有效位,那么最高有效位一定会造成负奉献,此时即使后面所有位都造成正奉献也抵消不了最高位的奉献,因此只需要考虑最高位即可

那么就是 f(x,z) 的 w 位是 0 这种情况了,那我们如何求呢?

我要快速知道 f(x,z),对于这种区间问题我们显然可以使用前缀和来求,我们定义 s[i] 为前 i 位的异或和,那么 f(x,z) = s[z] ^ s[x - 1],因为根据异或性质可以知道 x ^ x = 0,所以我们异或掉前面的数就相当于删去,哪知道这个我们如何解决问题呢?

由于我们要保证 f(x,z) 的 w 位是 0,那么也就是说 s[z] 和 s[x - 1] 的 w 位都是 0/1,所以我们还需要一个前缀和 onehas[i][j] 来统计 前 i 个 s[i] 中第 j 位是 1 有多少个选择(位置),而 0 的位置我们只需要用当前位置减去 1 的数量即可

因此最后我们只需要这样写,我们枚举每个 a[i] 当作 y ,然后利用乘法原理来计算答案,具体的我们统计 i 位置前有多少个 s[i] 的 w 位是 0/1(以及 i 位置后),然后二者相乘即可,因为我们不需要知道具体的值,我们只需要知道我们选的两个端点 z x 的位置是不一样的即可,所以利用浅醉和计算符合条件的位置的数量是个很快的方法

具体实现看代码

代码:

#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"void solve()
{auto getw = [](int x) ->int{for (int i = 30; i >= 0; i--){if (x >> i & 1)return i;}return 0;};int n;cin >> n;vector<int> a(n+1,0);//前缀和vector<int> s(n + 1, 0);//在前 j 个前缀和里有 onehas个数是 第 i 位是 1vector<vector<int>> onehas(31, vector<int>(n+1, 0));for (int i = 1; i <= n; i++){cin >> a[i];s[i] = s[i - 1] ^ a[i];for (int j = 30; j >= 0; j--){onehas[j][i] = onehas[j][i - 1] + (s[i] >> j & 1);}}int res = 0;for (int i = 1; i <= n; i++){//取得 a[i] 的最高位是多少int w = getw(a[i]);res += onehas[w][i - 1] * (onehas[w][n] - onehas[w][i - 1]);res += (i - onehas[w][i - 1]) * (n - i + 1 - (onehas[w][n] - onehas[w][i - 1]));}cout << res << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}

相关文章:

  • 《从零开始:构建你的第一个区块链应用》
  • Spring 6.x 详解介绍
  • STM32单片机的快速成长路径规划
  • 深入解析C++11 auto 关键字:类型推导的现代实践
  • uniapp小程序中实现无缝衔接滚动效果
  • Nginx高级配置
  • 用go从零构建写一个RPC(仿gRPC,tRPC)--- 版本2
  • 宝蓝德中间件部署war包时,配置的绝对路径读取错误。
  • LabVIEW多通道并行数据存储系统
  • llama.cpp无法使用gpu的问题
  • 基于CNN卷积神经网络的带频偏QPSK调制信号检测识别算法matlab仿真
  • Redis 集群
  • 视觉革命来袭!ComfyUI-LTXVideo 让视频创作更高效
  • Java常用类概述
  • 【STM32 学习笔记】USART串口
  • 【验证码】⭐️集成图形验证码实现安全校验
  • Skyvern:用 AI+视觉驱动浏览器自动化
  • Flink 实时数据一致性与 Exactly-Once 语义保障实战
  • 安装或更新 CUDA Toolkit - Ubuntu - RuntimeError
  • 翻转数位题目解释和代码
  • 水豚出逃40天至今未归,江苏扬州一动物园发悬赏公告
  • 1至4月全国铁路完成固定资产投资1947亿元,同比增长5.3%
  • 人民日报读者点题·共同关注:今天我们为什么还需要图书馆?
  • 让“五颜六色”面孔讲述上海故事,2025年上海城市推荐官开启选拔
  • 中国金茂新任命三名副总裁,撤销区域公司
  • 2025年度上海市住房城乡建设管理委工程系列中级职称评审工作启动