1. 算数
题目描述:
小美在数学课上学会了加减乘除,现在她想多进行一些算术训练来加强自己的计算能力。为了不重复出题,她想出一个好方法。她先写下了一排n个数(n>=2),依次用加号连接。举例来说,小美可能写下了如下的式子1+4+7+4+2+3+1共七个数以及6个加号。接着小美以一种全新的方式进行出题:她每次选择一个加号,将它改变成加减乘除中的一个(虽然很奇怪,但小美认为加号也可以被改成加号,尽管不会产生任何影响)然后计算整个式子的结果。值得注意的是,小美认为每次操作不对后续操作产生影响。
小美认真做了很多次算术训练,现在她想让作为她好朋友的你帮她用程序计算一次,方便她核对答案
输入描述:
第一行一个整数n
接下来一行n个整数a1,a2,...,an,依次表示小美初始写下的连加算式中的每一个数
接下来一个整数m,表示小美做了m次算术训练
接下来2m个以空格分开数字或符号t1,o1,t2,o2,...,tm,om,其中ti为数字,oi是'+','-','*','/'(即加减乘除符号,不含引号)中的一个符号,表示第i次操作选定了第ti个加号,将其改变为了oi
对于所有的数据,2<=N<=50000,1<=M<=50000,1<=ai<=500,1<=ti<N
输出描述:
输出一行m个整数,分别表示每次操作的答案,结果四舍五入到一位小数
样例输入:
5
1 2 4 2 5
3
1 -
2 *
4 /
样例输出:
10.0
16.0
7.4
思路:打卡题,搞个累加和,处理符号两边的数字即可
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<double> nums(n);
double sum = 0;
for (int i = 0; i < nums.size(); i++) {
cin >> nums[i];
sum += nums[i];
}
int m;
cin >> m;
for (int i = 0; i < m; i++) {
int t;
char o;
cin >> t >> o;
double res = sum - nums[t - 1] - nums[t];
double temp;
if (o == '+') {
temp = nums[t - 1] + nums[t];
}
else if (o == '-') {
temp = nums[t - 1] - nums[t];
}
else if (o == '*') {
temp = nums[t - 1] * nums[t];
}
else {
temp = nums[t - 1] / nums[t];
}
res += temp;
printf("%0.1f\n", res);
}
return 0;
}
2. 整理
题目描述:
小美正在整理桌子上的一排装饰品,小美对待装饰品摆放方式的审美角度很奇特,她认为高度相差比较大的装饰品放在相邻位置会很难看,她想对这一排装饰品进行整理,可以交换任意两个装饰品的位置任意多次。假设当前从左到右n个装饰品的高度分别为h1,h2,…,hn,那么当前这一排装饰品的丑陋值为,其中|x|为x的绝对值。小美想最小化她的装饰品丑陋值,请你帮她排一下顺序。
形式化地来讲,有一长为n的序列a1,a2,…,an,你可以任意次数地进行交换,每次交换都可以选择任意两个不同的数i,j交换ai,aj的位置。假设经过若干次交换后,序列变为h1,h2,...,hn,其丑陋值为,你只需要找出一种交换方式,使得最终序列{hn}的丑陋值最小化。你不需要输出具体交换方式,只需要输出最终的{hn}序列的丑陋值即可
输入描述:
第一行一个整数n,表示小美的装饰品数量。
接下来一行n个整数a1,a2,……,an,依次表示从左到右n个装饰品的高度
对于所有的数据:2<=N<=50000,0<=ai<=10^9
输出描述:
输出第一行一个数,为最优方案的最小丑陋值
样例输入:
3
3 1 2
样例输出:
2
思路:sort完事,顺序排下来就是最小
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int n; cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
sort(nums.begin(), nums.end());
long long res = 0;
for (int i = 1; i < n; i++) {
res += nums[i] - nums[i - 1];
}
cout << res;
return 0;
}
3. 收藏家
题目描述:
小美爱上了收藏!现在她给自己修建了一排n个收藏夹分别编号为1,2,3,……,n
有时小美会改变某一个收藏夹里的内容,例如从中拿入、拿出一些藏品,这样的操作会改变小美对这个收藏夹的欣赏程度,我们记编号为i的收藏夹小美对其的欣赏程度为ai。还有一些时候,小美会欣赏连续编号的一些收藏夹,例如编号为L,L+1,L+2,……,R-1,R的这一些收藏夹,她能从中获得的满足感为这些收藏夹的欣赏程度之和。
小美想在欣赏之前提前做一些评估,想知道如果她选择编号区间为[L, R]的收藏夹,能给她带来的满足感是多少。小美想请你,最好的朋友,来帮帮她
输入描述:
第一行两个整数n和m,表示小美的收藏夹数量和小美的操作数量。初始时刻收藏夹都是空的,也即ai=0
第二行m个整数op1,op2,……,opm
第三行m个整数x1,x2,……,xm
第四行m个整数y1,y2,……,ym,这些共同表示了m次操作。具体而言,对第i次操作,opi=0时表示为一次收藏夹更新操作,会将xi位置的收藏夹欣赏程度更新为yi,即axi=yi;opi=1表示为一次查询操作,表示如果小美欣赏编号在区间[xi, yi]的收藏夹,能获得的满足感是多少
对于所有的数据,1<=n,m<=50000, opi属于{0,1}。当opi=0时,1<=xi<=n,0<=yi<=10000;当opi=1时,1<=xi<=yi<=n。保证至少有一次opi=1的操作
输出描述:
对每个opi=1的操作,输出一个数表示对应答案。空格隔开所有答案
样例输入:
4 7
1 0 1 0 1 0 1
1 1 1 3 1 4 1
3 2 3 5 3 100 3
样例输出:
0
2
7
7
思路:线段树(我用前缀和做的,可能会超时,后面写篇文章补充下线段树做法)
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int n; cin >> n;
vector<int> nums(n);
int m; cin >> m;
vector<vector<int>> terms(3, vector<int>(m, 0));
for (int i = 0; i < 3; i++) {
for (int j = 0; j < m; j++) {
cin >> terms[i][j];
}
}
for (int i = 0; i < m; i++) {
if (terms[0][i] == 0) {
int x = terms[1][i] - 1;
int y = terms[2][i];
for (int j = x; j < n; j++) {
nums[j] += y;
}
}
else {
int x = terms[1][i] - 1;
int y = terms[2][i] - 1;
if (x == 0) {
cout << nums[y] << endl;
}
else {
cout << nums[y] - nums[x - 1] << endl;
}
}
}
return 0;
}
4. 倒水魔法
题目描述:
魔法训练室里有n个神奇的杯子,有着不同的大小,假设第i个被子已满,向其倒水,多余的水会正正好好流向第i+1个杯子。(如果i=n时没有下一个杯子,不会有杯子接住此时多余的水而溢出到魔法训练室的水池)
这些杯子有着初始固定的水量,每次练习后杯子里的水都会还原到最初状态。每次练习时,魔法黑板会告诉小美需要将哪一个杯子到满水。因为每个杯子的材质和形状有所不同,所以对其释放倒水魔法需要消耗的魔法值不同,小美想尽可能练习,所以需要最小化每次消耗的魔法值的总量
输入描述:
第一行一个整数n,表示杯子数量
第二行n个整数x1,x2,……,xn,依次表示第i个杯子能容纳水的量
第三行n个整数y1,y2,……,yn,依次表示第i个杯子初始有的水量
第四行n个整数z1,z2,……,zn,依次表示对第i个杯子每添加1毫升水需要消耗的法力值
第五行一个整数m,表示练习的数量
第六行m个整数q1,q2,……,qm,依次表示第i次练习时需要将第qi个杯子倒满(每次练习过后,杯子里的水量都会还原为初始状态,不会影响到下一次练习)
1<=n,m<=3000, 1<=yi<=xi<=109, 1<=zi<=300, 1<=qi<=n
输出描述:
输出第一行m个数,依次表示每次训练时需要消耗的最小法力值。如果杯子初始本身就是满的,则需要消耗的法力值为0
样例输入:
3
1 2 3
1 1 2
1 2 5
2
3 1
样例输出:
2 0
思路:前缀和,枚举区间可能会消耗时间复杂度,但是没想到啥好方法
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int n; cin >> n;
vector<int> fullSize(n);
int sum = 0;
for (int i = 0; i < n; i++) {
int temp;
cin >> temp;
sum += temp;
fullSize[i] = sum;
}
vector<int> nowSize(n);
sum = 0;
for (int i = 0; i < n; i++) {
int temp;
cin >> temp;
sum += temp;
nowSize[i] = sum;
}
vector<int> cost(n);
for (int i = 0; i < n; i++) {
cin >> cost[i];
}
int m;
cin >> m;
int res = INT_MAX;
for (int i = 0; i < m; i++) {
int temp;
cin >> temp;
temp--;
for (int j = 0; j < temp; j++) {
res = min(res, ((fullSize[temp] - fullSize[j]) - (nowSize[temp] - nowSize[j])) * cost[j + 1]);
}
res = min(res, (fullSize[temp] - nowSize[temp]) * cost[0]);
cout << res << " ";
}
return 0;
}
5. 价值
题目描述:
你现在有一棵树,树上的每个节点都有自己的价值。价值的计算规则如下所示:
1. 若某节点N没有儿子节点,那么节点价值为1
2. 若某节点N有两个儿子节点,那么节点N价值为两个儿子节点的价值之和,或者价值值按位异或。这取决于节点N的颜色,若N的颜色为红色,那么节点N价值为两个儿子节点价值之和;若N的颜色为绿色,那么节点价值为两个儿子节点的价值之按位异或
保证这棵树要么没有儿子节点,要么有两个儿子节点
输入描述:
第一行一个正整数n表示节点个数
第二行n-1个正整数p[i](2<=i<=n)表示第i个节点的父亲。1号节点是根节点
第三行n个整数c[i](1<=i<=n),当c[i]=1时表示第i个节点是红色的,c[i]=2则表示绿色
对于所有的数据,n<=50000
输出描述:
输出一行一个整数表示根节点的值
样例输入:
3
1 1
2 2 2
样例输出:
0
思路:树迭代
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int dfs(vector<vector<int>> &trees, vector<int> &color, int root) {
if (trees[root].size() == 0) return 1;
if (color[root] == 1) {
return dfs(trees, color, trees[root][0]) + dfs(trees, color, trees[root][1]);
}
else {
return dfs(trees, color, trees[root][0]) ^ dfs(trees, color, trees[root][1]);
}
}
int main() {
int n; cin >> n;
vector<vector<int>> trees(n + 5);
for (int i = 2; i <= n; i++) {
int temp; cin >> temp;
trees[temp].push_back(i);
}
vector<int> color(n + 5);
for (int i = 1; i <= n; i++) {
cin >> color[i];
}
cout << dfs(trees, color, 1);
return 0;
}