前排感谢@范总、@黄总两位大佬的支持。
1.积木-5
给你 n 堆积木,第 i 堆积木有 a[i] 个,现在你需要处理以下两种操作: 1 X Y :给第X堆的积木添加 Y 个,即a[X] += Y。 2 L R :求出区间[L,R]内积木的最大值。
RMQ(Range Minimum/Maximum Query)模板题,使用线段树/ST算法。
2.吃饼干
有 m 块饼干,每天至少吃一块,至多全吃完,问总共有多少种吃法。
依据题意,我们很容易写出递归函数代码
void f(int m)
{
if (m == 0)cnt++;
for (int i = 1; i <= m; i++)
{
f(m - i);
}
}
但是,本题最大输入为100,必定超时。该怎么办呢?我们观察本题较小数字的结果1——1、2——2、3——4、4——8、5——16。很容易观察得出本题的答案是2^(m-1)。
做到这里,本题的坑还没有结束,2的100次方三十多位,远远超过LLONG_MAX。此时需要使用高精度运算。
关于高精度运算的介绍:【高精度运算】高性能BigInt类 支持负数(内存优化版)
3.tql捕鱼-3
依旧是那个高端的捕鱼设备,tql 又来捕鱼了,这一次,鱼儿们决定在 n 行 n 列的海面中均匀分布,即每个格子中鱼的数量都相等。
tql 依然可以选择一个格子进行撒网,然后把这个格子所在行和所在列的鱼全部捕获,你有幸作为 tql 的小助手,负责清点 tql 每次捕获的鱼的数量。
注意:被捕获后,对应的格子就没有鱼了。
本题的数据量极大,故不可能使用数组记录鱼分布情况。依题意,在选择m行m列的格子后,同一行和同一列的鱼全部被捕,被捕的鱼的数量为2m-1只。在多次捕获后,选定的格子所在的行或列可能已经被捕获过所以没有鱼了,这又要另外统计。
一共有四种可能性:
1.选定的格子之前已经被选过,捕获的鱼的数量为0
2.选定的格子所在行被捕获过,捕获的鱼的数量为m减所在行被选定的格子数
3.选定的格子所在列被捕获过,捕获的鱼的数量为m减所在列被选定的格子数
4.选定的格子所在行和列均未被捕获过,捕获的鱼的数量为2m-1
int main()
{
set<int>sx; set<int>sy;
int n, m;
cin >> n >> m;
int cntx = n, cnty = n;
while (m--) {
int x, y;
cin >> x >> y;
int tx = sx.count(x);
int ty = sy.count(y);
if (tx && ty) {
cout << 0;//选定的格子之前已经被选过,捕获的鱼的数量为0
}
else if (tx && ty == 0) {
cout << n - sx.size();
}
else if (tx == 0 && ty) {
cout << n - sy.size();
}
else{
cout << 2 * n - 1 - sy.size() -sx.size();//选定的格子所在行和列均未被捕获过,捕获的鱼的数量为2m-1
}
sx.insert(x);
sy.insert(y);
cout << endl;
}
}
4.派件问题-2
罗少有 n 件物品需要派送,但是他太懒了,于是他想找若干个小弟来帮忙。
已知每个小弟的服务费由两部分组成:跑腿费 s 元 + t * t 元,其中 t 代表这个小弟携带的物品件数。
请问罗少至少需要花多少钱才能把这 n 件物品派送完。
本题解析请移步《对极值问题的探索》。
5.质数问题
设 f(n) = 比 n 小的最大质数,现在给定两个正整数 L 和 R,求对于区间 [ L,R ] 内的每一个正整数 x,f(x) 之和。
线性筛+差分数组。
首先用线性筛筛出质数,可以得到prime数组(存放所有质数)。依题意易得,从prime[i]+1到prime[i+1]的比n小的最大质数都是prime[i]。我们只需要写双重循环给数组赋值就可以了。
for (int i = 1; i <= prime[0]; i++)
{
for (int num=prime[i]+1; num <= prime[i+1]; num++)visit[num] = prime[i];
}
求区间内的和,需要用到差分数组,相关题目见链接,不再赘述。
直接上代码:
#include<iostream>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn = 1000035;
int prime[maxn];
ll visit[maxn];
void Prime() {
for (int i = 2; i <= maxn; i++) {
if (!visit[i]) {
prime[++prime[0]] = i;
}
for (int j = 1; j <= prime[0] && i * prime[j] <= maxn; j++) {
visit[i * prime[j]] = 1;
if (i % prime[j] == 0) {
break;
}
}
}
}
int main()
{
Prime();
memset(visit, 0, sizeof(visit));
for (int i = 1; i <= prime[0]; i++)
{
for (int num = prime[i] + 1; num <= prime[i + 1]; num++)visit[num] = prime[i];
}
for (int i = 1; i <= 1000007; i++) { visit[i] = visit[i - 1] + visit[i]; }
int T;
cin >> T;
while (T--)
{
int a, b;
cin >> a >> b;
cout << visit[b] - visit[a - 1] << endl;
}
return 0;
}
6.二进制拼接
将区间[L,R]之间的数字用二进制表示,并拼接成一段新的二进制数串,求这个二进制数串对应的十进制。
对于[L,R]之间数字i的拼接,可以分为两部分[L,i-1]和i来考虑。
设[L,i-1]的十进制为ans,[L,i]的结果就是ans<<k+i(ans的二进制后面接上i的二进制),k是二进制下i的长度。怎么求k呢?
如何求k?
对于两个数字x,x-1,他们二进制下的位数无非是相同或者差1,当x是2的幂次方的时候,他们就相差1,所以我们就可以利用递推式,去累加位数k,那么这个题就转化成如何判断数字x是否是2的幂次方。
如何判断x是否是2的幂次方?
x&(x-1)是个很神奇的操作,他可以让你的最右边一位的1变成0,如果最后一位1变成0变成0了,说明他只有最左边的1,因此就是2的幂次方。
——Fanxw
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
int cnt = 0;
int first = a;
while (first)//求出第一个数多少位
{
cnt++;
first >>= 1;
}
long long ans = a;
for (int i = a+1; i <= b; i++)//从a+1个数开始算,而不是第a个数
{
if ((i & (i - 1)) == 0)cnt++;//i&i-1如果等于0,就说明二进制数位数+1
ans <<= cnt;
ans += i;
ans %= 1000000007;
}
cout << ans << endl;
return 0;
}