问题 A: 活动安排
题目描述
小明又被安排去处理公司大厅事项了,公司只有一个大厅,但有许多的活动需要召开,所以需要提前预约,该公司有n个部门,每个部门会举办一个活动。每个部门会向小明上报他们预期使用大厅的时间区间 (a,b)(开区间),小明会根据时间做安排,一个时间只能有一个活动在召开,活动一旦开始必须结束才能进行下一个活动。
小明想要尽可能多的满足活动的召开,于是请你来帮忙安排活动。
输入
第一行一个整数n,代表部门的个数;
接下来的nn 行,每行两个整数a,b,代表时间区间。
其中:1≤n≤500000,0≤a<b≤1e9
输出
输出一个整数代表能够召开的最大活动个数。
输入输出样例
样例输入 #1
4
1 3
4 6
2 5
1 7
样例输出 #1
2
这是很可惜的一道题,因为第一次考核就出了差不多类型的题(贪心算法,区间问题)。当时囫囵吞枣只看了那题的解答,甚至没有理解透彻。
问题 B: 最大异或和
题目描述
给定一串手链,每个珠子被赋予一个价值w[i],现要从中截取连续的一段,使得他们的异或和最大(注意,手链还未串上)。
输入
第11行包含一个正整数N;
第22行n个正整数w[i],表示珠子价格。
其中:n≤2000,w[i]≤100000
输出
一个正整数,输出最大异或和。
输入输出样例
样例输入 #1
5
1 2 3 4 5
样例输出 #1
7
这个题我是想着用动态规划型做,因为真的很像子序列的题,但是试了好几遍都错了。
后面问了一下才知道要用前缀异或 (处理思路类似于前缀和),而且要分几个情况讨论。
其中,因为一个数和自己异或直接得0,这是能用前缀异或写的重要原因。
cpp
#include <iostream>
#include<vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int>a(n);
for (int i = 0;i < n;i++)
cin >> a[i];
vector<int>f(n);//前缀异或数组,类似于前缀和
f[0] = a[0];//不要忘了初始化
for (int i = 1;i < n;i++)
f[i] = f[i - 1] ^ a[i];
int ans = 0;
//单元素区间
for (int i = 0;i < n;i++)
ans = max(ans, a[i]);
//从第一个元素开始的区间
for (int i = 0;i < n;i++)
ans = max(ans, f[i]);
//多元素区间
for (int i = 0;i < n;i++)
{
for (int j = i + 1;j < n;j++)
{
//f[i]^f[j] 是区间 [i+1, j] 的异或和
ans = max(ans, f[i] ^ f[j]);
}
}
cout << ans;
return 0;
}
问题 C: 救命!橘子学长的游园数据全乱了
题目描述
一年一度的五一假期如约而至,各地景区迎来大规模出游人流。橘子学长趁着小长假出门游玩,来到了热门网红园区。
园区入园道路笔直狭长,沿途划分了 n 个等候点位,编号为 1,2,...,n,每个点位都停留着等候入园的游客。我们为第 i 个点位定义一个游客偏好系数 ai(满足 ∣ai∣≤1000):正数代表游客行程紧张,想要快速入园;负数代表游客节奏舒缓,愿意错峰排队;数值为零则表示无特殊等候偏好。
园区工作人员会任选一个点位 x 作为临时集合点,统一集合游客后分批入园。对于处在第 i 号点位的游客,移动到集合点 x 的出行消耗为 ai*∣i−x∣,所有游客的消耗总和为全局总代价 f(x),满足:f(x)=a1*∣1−x∣+a2*∣2−x∣+a3*∣3−x∣+⋯+an*∣n−x∣
游玩途中细心的橘子学长,悄悄记录下了集合点设在 1∼n 每个位置时的全部总代价数据,也就是 f(1),f(2),...,f(n)。但可惜记录草稿不慎错乱,原本对应的各点位偏好系数序列 a1,a2,...,an 完全遗失。
请你帮助橘子学长,依靠现有的 f 序列数据,反向推理并还原出完整的 a 序列。题目保证答案唯一。
输入
多组测试数据。第一行给定整数 t,代表测试组数。
每组数据第一行输入整数 n;
第二行输入 n 个整数,依次为 f(1),f(2),...,f(n)。
保证所有测试用例的 n 总和不超过 3×10^5。
输出
每组测试用例输出一行 n 个整数,为序列 a1,a2,...,an。
输入输出样例
样例输入 #1
4
4
17 9 9 13
6
-37 -32 -15 4 27 42
5
-26 -32 -24 -4 2
2
420 -69
样例输出 #1
1 4 2 3
3 6 1 2 -4 -7
-6 7 6 -7 -6
-69 420
提示
在第一个测试用例中,隐藏的序列为 a=[1,4,2,3]。
f(1),f(2),...,f(n) 的值计算如下:
- f(1)=1⋅∣1−1∣+4⋅∣2−1∣+2⋅∣3−1∣+3⋅∣4−1∣=0+4+4+9=17;
- f(2)=1⋅∣1−2∣+4⋅∣2−2∣+2⋅∣3−2∣+3⋅∣4−2∣=1+0+2+6=9;
- f(3)=1⋅∣1−3∣+4⋅∣2−3∣+2⋅∣3−3∣+3⋅∣4−3∣=2+4+0+3=9;
- f(4)=1⋅∣1−4∣+4⋅∣2−4∣+2⋅∣3−4∣+3⋅∣4−4∣=3+8+2+0=13。
问题 D: 橘子学长的扬州自驾之旅
题目描述
橘子学长最近刚考完驾照,又恰逢暑假将至,便决定自驾去扬州旅行。一来可以欣赏烟雨扬州的江南美景,二来还能磨炼驾驶技术。
橘子学长查阅各种旅游资料后,精心规划出总行程为 L 的路线。由于路途较远,橘子学长必须在沿途多次加油才能跑完全程。在他规划的这条路线上共计 N 个加油站,每个加油站都可以直接把油箱加满 。第 i 个加油站位于位置 Pi,在这里加满油箱需要花费 Ci 元,同一个位置可能有多个加油站。
橘子学长的车油箱上限为 Maxn,出发时油箱是满的。车辆每消耗 1 单位油,可以行驶 1 单位路程。当油量恰好为 0 时,如果当前位置有加油站,仍然可以加油;否则车辆会抛锚,无法继续前进。
橘子学长一共准备了 S 元作为加油预算,他想知道自己能否顺利完成这次扬州自驾游。
输入
输入第一行一个正整数 t,表示数据组数。
对于每组数据:第一行输入四个整数 N,L,Maxn,S;
接下来 N 行,每行输入两个整数 Pi,Ci,Pi 不一定按顺序给出。
输出
每组数据输出一行。如果橘子学长能够完成自驾游,输出 Yes;否则输出 No。
输入输出样例
样例输入 #1
2
1 1000 500 2000
500 2000
1 1000 500 2000
500 3000
样例输出 #1
Yes
No
提示
- 1≤t≤100
- 0≤N≤2000
- 0≤L≤20000
- 0≤Maxn≤20000
- 0≤S≤2000
- 1≤Pi≤L
- 1≤Ci≤3000
问题 E: 曹冲不想看数学题
题目描述
曹冲接到的另一个任务是给定两个正整数 n 和 m,判断:在 1 到 m 的所有整数中,是否存在两个不相等的数 x 和 y(满足 1≤x<y≤m),使得 n 除以 x 的余数,与 n 除以 y 的余数完全相同。 曹冲更是不会做,又来请你帮忙。
如果存在这样的两个数,输出 Yes;否则输出 No。
输入
输入包含多组独立测试数据。 第一行输入一个整数 T,表示测试数据的组数。
接下来 T 行,每行输入两个整数 n,m,用空格分隔,表示一组询问。
1≤T≤1e5, 1≤n≤1e9, 2≤m≤1e9。
输出
对于每组询问,输出一行结果:存在满足条件的数则输出 Yes,否则输出 No。
输入输出样例
样例输入 #1
3
1 2
5 2
999 99
样例输出 #1
No
No
Yes
刚开始也是想暴力两层循环,但是想想这样O(N^2)可能会超时,刚好哈希表又可以存映射,做到O(N)的复杂度,为什么不用哈希表呢哈哈哈,而且哈希表也不难写。
cpp
#include <iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<unordered_map>
using namespace std;
bool judge(int n,int m)
{
unordered_map<int,int>mp;
for(int x=1;x<=m;x++)
{
int mol=n%x;
if(mp[mol]>0)
return true;
mp[mol]++;
}
return false;
}
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
if(judge(n,m))
cout<<"Yes\n";
else
cout<<"No\n";
}
return 0;
}
问题 F: 曹冲不会数学题
题目描述
曹冲最近接到了两个任务,一个是生成字符串的任务,任务需要他把 n 个 1 和 m 个 0 组成字符串,但是任务还要求在组成的字符串中,在任意的前 k (0≤k ≤n +m) 个字符中,1 的个数不能少于 0 的个数。现在曹冲想要知道满足要求的字符串共有多少个,但是曹冲的数学实在是太差了,聪明的程序员们,你们能帮助他吗?
答案对 20100403 取模。
输入
输入数据只有一行,包括 2 个数字 n 和 m 。1≤m ≤n≤1e6。
输出
输出数据是一行,包括 1 个数字,表示满足要求的字符串数目。
答案对 20100403 取模。
输入输出样例
样例输入 #1
2 2
样例输出 #1
2
因为这题我也没找到规律,看到结果很大要取模,所以考场上我蒙的快速幂。
这题是真难啊,说是括号匹配 的变种,要用卡特兰数 做。组合数 还不能直接用动态规划做,因为数据范围是1e6,开1e6*1e6的数组内存会直接炸掉。得线性求逆元,所以还要用到数论的知识。我是真不会了,也不怪我当时没写对,这题真的太难了。。。下面贴一份AI写的代码。
cpp
#include <iostream>
#include <vector>
using namespace std;
typedef long long LL;
const int MOD = 20100403;
const int MAX = 2000005; // n+m ≤ 2e6
LL fact[MAX], invFact[MAX], inv[MAX];
// 计算组合数 C(a, b) mod MOD
LL comb(int a, int b) {
if (b < 0 || b > a) return 0;
return fact[a] * invFact[b] % MOD * invFact[a-b] % MOD;
}
int main() {
int n, m;
cin >> n >> m;
int N = n + m; // 最大阶乘值
// 1. 预处理阶乘
fact[0] = 1;
for (int i = 1; i <= N; i++) {
fact[i] = fact[i-1] * i % MOD;
}
// 2. 线性求逆元
inv[1] = 1;
for (int i = 2; i <= N; i++) {
inv[i] = MOD - inv[MOD % i] * (MOD / i) % MOD;
}
// 3. 求阶乘逆元
invFact[0] = 1;
for (int i = 1; i <= N; i++) {
invFact[i] = invFact[i-1] * inv[i] % MOD;
}
// 4. 计算结果
LL ans = (comb(n+m, n) - comb(n+m, n+1) + MOD) % MOD;
cout << ans << endl;
return 0;
}
问题 G: 每日温度
题目描述
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
1 <= temperatures.length <= 100'00030 <= temperatures[i] <= 100
输入
第一行输入N;
第二行输入N个数
输出
输出一行整数
输入输出样例
样例输入 #1
8
73 74 75 71 69 72 76 73
样例输出 #1
1 1 4 2 1 1 0 0
当时暴力写了一遍两层循环的,超时了。我后面还做了一些剪枝,找到第一个答案就break,仍旧超时。
我重新审视了这道题,看着很像动态规划。但是想着动态规划却没想到思路,所以这题就一直留在后面不敢碰。直到最后十分钟的时候,所有会的题都写完了。闲着也是闲着,就把之前跳过的且尝试过题目都看了一遍。
当时我就感觉应该可以用一个数据结构来存还没找到答案的数,以后有比它大的数就把它的答案设置成那个数的下标减去它的下标。然后这个数据结构里存的数的下标应该是递增的,如果下次筛选符合了就把这个数从这个数据结构移走。这样就可以做到几乎线性的时间复杂度。
没想到居然还能自己生搬硬套现场想了一个栈。现在想想感觉挺牛逼的,而且当时改完这版代码就最后四分钟了,已经没有试错的成本了,更让我意想不到的是交上去居然一把过。
后面问了一下,学名叫单调栈。
cpp
#include <iostream>
#include<string>
#include<vector>
#include<stack>
#include<algorithm>
#include<unordered_map>
using namespace std;
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int n;
cin>>n;
vector<int>a(n);
for(int i=0;i<n;i++)
cin>>a[i];
vector<int>ans(n,0x3f3f3f);
stack<int>st;
stack<int>pos;
for(int i=1;i<n;i++)
{
if(a[i]>a[i-1])
{
while(!st.empty())
{
int t=st.top();
int t_p=pos.top();
if(a[i]>t)
{
st.pop();
pos.pop();
ans[t_p]=i-t_p;
}
else
break;
}
ans[i-1]=1;
continue;
}
else
{
st.push(a[i-1]);
pos.push(i-1);
}
}
for(int&e:ans)
{
if(e==0x3f3f3f)
e=0;
}
for(int e:ans)
cout<<e<<" ";
return 0;
}
当然,我这个代码虽然AC了,但实现细节有些问题,还有优化空间:
1.不必要的头文件
2.用一个栈存下标 就够了,温度可以通过
a[下标]获取,用两个栈增加了内存和操作复杂度3.应该处理所有栈中比当前温度小的 ,不只是当
a[i] > a[i-1]时
cpp
#include <iostream>
#include<vector>
#include<stack>
using namespace std;
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int n;
cin >> n;
vector<int>a(n);
for (int i = 0;i < n;i++)
cin >> a[i];
vector<int>ans(n, 0);
stack<int>st;
for (int i = 0;i < n;i++) // 从0开始
{
// 关键:当前温度a[i]尝试解决栈中所有比它小的
while (!st.empty() && a[i] > a[st.top()])
{
int pos = st.top();
st.pop();
ans[pos] = i - pos;
}
// 每个元素都要入栈等待
st.push(i);
}
for (int e : ans)
cout << e << " ";
return 0;
}
问题 I:最大质量降落
题目描述
给定一个正整数 n 。你需要找到,在所有使得 gcd(1×p 1,2×p 2,⋯,n ×pn ) 的值尽可能大的 1∼n 的排列 p 中,字典序最小的排列 p。
其中:
- 1∼n 的排列表示长度为 n 且 1∼n 均恰好出现一次的序列;
- gcd(x 1,x 2,⋯,xn ) 表示 x 1,x 2,⋯,xn 的最大公因数;
- 对于两个 1∼n 的排列 a,b,a 的字典序比 b 小,当且仅当存在一个正整数 i ,满足 a 的前 i −1 项与 b 的前 i −1 项均相同且 ai <bi。
对于所有测试数据,2≤n≤1000000。
输入
输入一行,包含一个正整数 n。
输出
输出一行,包含 n 个正整数,表示满足条件的 1∼n 的排列 p。
输入输出样例
样例输入 #1
2
样例输出 #1
2 1
样例输入 #2
3
样例输出 #2
1 2 3
提示
样例解释 #1
- 当 p={1,2} 时,gcd(1×1,2×2)=1;
- 当 p={2,1} 时,gcd(1×2,2×1)=2;
所以 {2,1} 为满足条件的排列 p。
样例解释 #2
- 当 p={1,2,3} 时,gcd(1×1,2×2,3×3)=1;
- 当 p={1,3,2} 时,gcd(1×1,2×3,3×2)=1;
- 当 p={2,1,3} 时,gcd(1×2,2×1,3×3)=1;
- 当 p={2,3,1} 时,gcd(1×2,2×3,3×1)=1;
- 当 p={3,1,2} 时,gcd(1×3,2×1,3×2)=1;
- 当 p={3,2,1} 时,gcd(1×3,2×2,3×1)=1;
所以 {1,2,3} 为满足条件的排列 p。
这题刚开始我看题干就被唬住了,要求整个序列的最大的最小公倍数的最小字典序。本来我想用next_permutation做全排列,然后一组数一组数慢慢求公约数。但是看到数据范围是1e6的时候感觉天都塌了,单纯全排列一次1e6!就足以超时 ,更别说这题要每一组数的最小公倍数了。因为这样暴力写只会贡献罚时,并没有任何价值,想都没想就跳过了。但后面我发现其实就是观察规律:n为奇数时直接原样输出,n为偶数时就分奇偶输出。
规律我当时也是按照测试样例瞎猜的,不太会描述,所以贴一段AI的:
-
当 n是奇数时,最大 gcd 只能是 1,因为奇偶性限制:
如果 d>1 包含质数 2,则 2|1p1 ⇒ p1 偶,同样 2|3p3 ⇒ p3 偶,等等,会需要奇数个偶数位置,但偶数数字只有 ⌊n/2⌋个,不够分,所以 d 不可能是偶数。
如果 d 是奇数质数,也需要类似条件,也容易矛盾。
所以 n 是奇数时,最大 d=1,排列只能是 1 2 ... n。
-
当 n是偶数时,我们可以做到 d=2,构造:
把 1..n 按奇偶分开,奇数在奇数位,偶数在偶数位,会得到 2|i*p_i 吗?
检查:i 奇时 p_i 偶,i 奇*p_i 偶 = 偶,能被 2 整除。
i 偶时 p_i 奇,偶*奇=偶,也能被 2 整除。
所以 d>=2。
字典序最小:第一位 p1 尽量小,但必须是偶数,所以 p1=2。
之后按**"奇数位放偶数,偶数位放奇数"**安排,并按最小可用数放。
cpp
#include <iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<stack>
using namespace std;
#define int long long
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int n;
cin>>n;
if(n%2==1)
{
vector<int>ans(n);
for(int i=1;i<=n;i++)
ans[i-1]=i;
for(int e:ans)
cout<<e<<" ";
}
else
{
vector<int>ans1;
vector<int>ans2;
for(int i=1;i<=n;i++)
{
if(i%2==1)
ans1.push_back(i);
else
ans2.push_back(i);
}
int m=ans1.size();
for(int i=0;i<m;i++)
{
cout<<ans2[i]<<" "<<ans1[i]<<" ";
}
}
return 0;
}
问题 J: 好数对的数目
题目描述
给你一个整数数组 nums 。
如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j,就可以认为这是一组 好数对 。
返回好数对的数目。
输入
第一行:整数 数组 nums的长度n(1<=n<=1000000)
第二行:n个数,代表整数 数组 nums(1<=nums[i]<=1000000000000)
输出
好数对的数目
输入输出样例
样例输入 #1
6
1 2 3 1 1 3
样例输出 #1
4
这题是原题。其实我刚开始也是哈希表的思路,每次测试样例都过了,但每次提交都答案错误。我真的觉得我0~n-1的下标范围没有错,明明把所有数都遍历到了啊。而且这一题是要看这个数之前有多少个数和它相等,我不加n才是正确的。真的百思不得其解。最后几分钟的时候我突然想到是不是循环里多挂个等号就对了,因为之前写作业好像解决方案就是多挂个n,因为就算哈希表里没有这个数,也不会存在访问下标超限的情况。抱着侥幸的心理赌了一把,真赌对了。但是时至今日我还是不能理解为什么要多挂个等号,到底是测试数据的问题还是我的问题?!
cpp
#include <iostream>
#include<string>
#include<vector>
#include<unordered_map>
#define int long long
using namespace std;
signed main()
{
int n;
cin>>n;
vector<int>a(n);
for(int i=0;i<n;i++)
cin>>a[i];
unordered_map<int,int>mp;
int cnt=0;
for(int i=0;i<=n;i++)
{
cnt+=mp[a[i]];
mp[a[i]]++;
}
cout<<cnt;
return 0;
}
问题 K: 测试数据生成器
题目描述
你正在开发一个测试数据生成器。给定两个整数 s 和 m,你需要构造一个由非负整数组成的数组 a = [a₁, a₂, ..., aₙ],使其满足以下两个条件:
数组中所有元素的和等于 s,即:a₁ + a₂ + ... + aₙ = s
对于数组中的每一个元素 aᵢ,都满足:aᵢ & m = aᵢ其中 & 为按位与运算符。换句话说,aᵢ 中所有为 1 的二进制位,都必须是 m 中同样为 1 的二进制位。
请你判断是否存在这样的数组。如果存在,请求出满足条件的数组的最小长度 n;如果不存在,输出 -1。
输入
本题包含多组测试数据。
第一行输入一个整数 t(1≤t≤1e4),表示测试数据的组数。接下来是各组测试数据的描述。
每组测试数据输入一行,包含两个整数 s 和 m(1≤s,m≤1e18),分别表示生成器的两个参数。
输出
对于每组测试数据,输出一个整数:
- 如果不存在满足条件的数组,输出
-1; - 否则,输出数组的最小可能长度 n。
输入输出样例
样例输入 #1
6
13 5
13 3
13 6
1000000007 2776648
99999999999 1
998244353 1557287
样例输出 #1
3
5
-1
-1
99999999999
642
问题 L: 爱丽丝与鲍勃的平面博弈
题目描述
爱丽丝和鲍勃在平面直角坐标系上玩点的游戏。初始时平面上有 n 个点,第 i 个点的坐标为 (xi,yi),权值为 ci。
游戏分为两个阶段:
- 第一阶段(爱丽丝操作) :爱丽丝选择若干个点 移除(可以不移除任何点,但不能移除所有点)。
- 第二阶段(鲍勃操作) :鲍勃画一个边与坐标轴平行 的矩形,要求所有剩余的点都在矩形内部或边界上。矩形可以退化成一条线段,甚至一个点。
游戏结束后计算总得分 :总得分 = 爱丽丝移除的所有点的权值之和 + 鲍勃画出的矩形的周长
爱丽丝想要最大化 总得分,鲍勃想要最小化 总得分。如果两人都采取最优策略,请你求出游戏的最终总得分。
输入
第一行一个整数 t(1≤t≤104)表示测试用例组数。
每组测试用例:
第一行一个整数 n(1≤n≤3⋅1e5)表示点的数量。
第二行 n 个整数 x1,x2,...,xn(0≤xi≤1e15)表示所有点的 x 坐标。
第三行 n 个整数 y1,y2,...,yn(0≤yi≤1e15)表示所有点的 y 坐标。
第四行 n 个整数 c1,c2,...,cn(0≤ci≤1e9)表示所有点的权值。
输出
对于每组测试用例,输出一个整数,表示双方最优策略下的最终得分。
输入输出样例
样例输入 #1
4
1
42
42
1000
4
5 10 5 0
0 5 10 5
1 1 1 1
4
6 7 8 9
3 3 3 3
9 0 9 0
2
1000000000 10
10 1000000000
12345 54321
样例输出 #1
0
40
22
3999999960
中间有个字符串拼接的题,最后还有两个素数题欧拉筛直接秒了,这种签到题就不放了。