蓝桥校选题解
一直忘了写,现在补一下
7-1 邪恶学长的代码 (5分)
第一题水题,直接输出样例就行,这里不写具体代码了
7-2 真正的送分题 (10分)
邪恶学长出的邪恶题目,坑点在数据范围0<=A<=2^64-1 ,0<=B<=2^64-1。即使用了longlong都会超范围,观察发现都是正数所以用无符号型刚好。unsigned long long,其他就是正常的加法。
7-3 tly的摩天轮 (15分)
这就是简单的计算题,套用向心力公式即可(分数出错了小失误,应该设置成10分的)
#include<iostream>
using namespace std;
int main() {
double r, v,m;
cin >> r >> v >> m;
printf("%.3lf",m*v*v/r);
}
7-4 谁是龙王2 (12分)
这题上次天梯校选出过,题目不够拿来用一下。
用一个map记录一下每个名字的出现一次,实时更新发言最多的人的名字就行。读入的时候要读入一整行,防止中间有空格啥的造成错误
没错就是我这么憨,数据没测完就交wa一发
#include <bits/stdc++.h>
#include<unordered_map>
using namespace std;
typedef pair<string, string> pss;
pss split(string str) {
string a;
string b;
int i;
i = 0;
while (i < str.size() && str[i] != ':') {
i++;
}
a = str.substr(0, i);
b = str.substr(i + 1);
pss p(a, b);
return p;
}
int main() {
int n;
cin >> n;
string a;
int i;
getchar();
unordered_map<string, int> mmid, map;
for (i = 0; i < n; i++) {
getline(cin, a);
pss temp = split(a);
mmid[temp.first]++;
if (!map[temp.first]) map[temp.first] = i + 1;
}
int maxn = 0;
string name = "";
for (auto& val : mmid) {
maxn = max(maxn, val.second);
}
int idx = n + 1;
for (auto& val : map) {
if (maxn == mmid[val.first]) {
if (idx > val.second) {
idx = val.second;
name = val.first;
}
}
}
cout << name << endl;
return 0;
}
这里普及一下unordered_map和map的区别,前者使用哈希表,后者是红黑树,相比而言unordered_map建立的耗时更长,但查询会快很多。map主要是有序性,很多操作在lgn的时间复杂度下就可以实现,因此效率非常的高。对于查找问题,unordered_map会更加高效一些。
7-5 爱瞎跑的学长 (20分)
一个三维的搜索题,写过博客了,这里就直接放链接了
https://blog.csdn.net/weixin_45439563/article/details/111793437
7-6 区间和 (25分)
分而治之的思想。
题目要求的是和大于子序列长度乘以x,所以我们预处理先将每一只减去x,这样只要求和为正数就是我们需要的,这样可以防止后面求和的数据过大超出范围。后面分治的想法,这里拿出一步举个例子方便理解。
l,r,mid已知,我们现在需要求的序列是横跨mid的,只有在左半边或者右半边的在long long ans = cdq(l, mid) + cdq(mid + 1, r);
这行中被计算,而超过这整一段的会在其他递归中计算。
求的是序列和,所以我们用前缀和处理数据,我们计算的横跨mid,所以左半边的前缀和从右往左,右半边的前缀和从左往右,然后枚举每一个左的点,看有几个序列合理即判断条件while (j > mid&& tmp[i] + tmp[j] >= 0) j--;
#include <cstdio>
#include <algorithm>
using namespace std;
int n, x, a[100010];
int tmp[100010];
long long cdq(int l, int r)
{
if (l == r) return a[l] >= 0;
int mid = (l + r) / 2;
long long ans = cdq(l, mid) + cdq(mid + 1, r);
tmp[mid] = a[mid];
tmp[mid + 1] = a[mid + 1];
for (int i = mid - 1; i >= l; i--)
tmp[i] = tmp[i + 1] + a[i];
for (int i = mid + 2; i <= r; i++)
tmp[i] = tmp[i - 1] + a[i];
sort(tmp + l, tmp + mid + 1);
sort(tmp + mid + 1, tmp + r + 1);
for (int i = l, j = r; i <= mid; i++)
{
while (j > mid && tmp[i] + tmp[j] >= 0) j--;
ans += r - j;
}
return ans;
}
int main()
{
scanf("%d%d", &n, &x);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), a[i] -= x;
printf("%lld\n", cdq(1, n));
return 0;
}
7-7 神之一手 (15分)
模拟题,先通过两位大佬的棋子数量对不对判断有没有作弊,然后横竖对角线判断有没有赢就行了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rd read()
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
vector<int> x[3];
int countsteps(int num)
{
int ans = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
ans += num == x[i][j];
}
}
return ans;
}
bool judge(int num)
{
int f = 0;
for (int i = 0; i < 3; i++)
{
if (x[i][i] != num)f = 1;//主对角
}
if (!f)return true;
f = 0;
for (int i = 0; i < 3; i++)
{
if (x[i][2-i] != num)f = 1;//副对角
}
if (!f)return true;
f = 0;
for (int i = 0; i < 3; i++)
{
int f = 0;
for (int j = 0; j < 3; j++)//横
{
if (x[i][j] != num)f = 1;
}
if (!f)return true;
}
f = 0;
for (int i = 0; i < 3; i++)
{
int f = 0;
for (int j = 0; j < 3; j++)//竖
{
if (x[j][i] != num)f = 1;
}
if (!f)return true;
}
return false;
}
int main()
{
string s;
int i;
for (i = 0; i < 3; i++)
{
cin >> s;
for (int j = 0; j < s.size(); j++)
{
int f = 1;
if (s[j] == '-')
{
j++;
f = -1;
}
x[i].push_back((s[j] - '0') * f);
}
}
int JXJ = countsteps(0);
int HDL = countsteps(1);
if (JXJ - HDL >= 2)
{
printf("Xie'eJXJ");
}
else if (HDL - JXJ >= 1)
{
printf("Xie'eHDL");
}
else if (judge(0))
{
printf("OrzJXJ");
}
else if (judge(1))
{
printf("OrzHDL");
}
else
{
printf("OrzLJL");
}
}
7-8 东方之旅-1 (15分)
dp模板,这里不多解释了,故事很好看
#include <cstdio>
#define max(a, b) ((a) > (b) ? (a) : (b))
int n, m, money[1010];
long long val[1010], dp[200000];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d%lld", &money[i], &val[i]);
for (int i = 1; i <= n; i++)
for (int j = m; j >= money[i]; j--)
dp[j] = max(dp[j - money[i]] + val[i], dp[j]);
printf("%lld", dp[m]);
}
7-9 东方之旅-2 (15分)
系列故事第二弹
并查集的题目,相比于平常的题目就是要记录一下一个团体的人数,还有团队的个数,还有发现两个熟人并且原本不熟的情况下,团体合并的时候要去掉一下团队的数量。
#include <bits/stdc++.h>
using namespace std;
const int N = 100050, M = 200100;
int n, m, num[N] = {}, now = 0, tot = 0, root[N + M] = {}, sizee[N + M] = {};
int Find(int r) {
if (r != root[r]) root[r] = Find(root[r]);
return root[r];
}
int main() {
scanf("%d%d", &n, &m);
now = tot = n;
for (int i = 1; i <= n; ++i) num[i] = root[i] = i, sizee[i] = 1;
int t, u, v;
for (int i = 1; i <= m; ++i) {
scanf("%d", &t);
if (t == 1) {
scanf("%d%d", &u, &v);
int ru = Find(num[u]), rv = Find(num[v]);
if (ru != rv) {
--tot;
root[ru] = rv;
sizee[rv] += sizee[ru];
sizee[ru] = 0;
}
}
if (t == 2) {
scanf("%d%d", &u, &v);
puts(Find(num[u]) == Find(num[v]) ? "Yes" : "No");
}
if (t == 3) {
scanf("%d", &u);
printf("%d\n", sizee[Find(num[u])]);
}
if (t == 4) printf("%d\n", tot);
}
}
7-7 东方之旅-3 斐波那契 (20分)
系列故事第三弹
这是一题思维题, 其实大家第一眼看过来会感觉 “这就是 LCA 问题嘛!
看我随便写一个模板 A 了。”然后发现这个建出来的树有点大。
就是一个找规律的题,我之前甚至在想,这个难度会不会比前面的简单,只是因
为剧情的原因放在最后面,结果发现还是有点思维量的,这个跟 CF 上面的 DIV2
的 B~C 的难度感觉差不多。
我简单的讲一下这个怎么做,这个图稍微观察一下
很明显 可以发现这除了根节点之外所有点的编号 减去他的父亲节点的编号 都
可以在斐波那契数列上面找到。
这个很容易证明,按照创建分身的时间来算,
time = 1 的时候第 2 号分身出来 = 1
time=2 的时候第 3 号分身出来 = 1
time = 3 的时候刚好是 2 号分身召唤分身的时候也就是 4 号和 5 号分身 = 2
time = 4 的时候是 6 号 7 号 8 号, = 3
然后把 time 建立成数列就是斐波那契数列了。
而根据这个思想 1 号分身的直接儿子是不是(1+1) (1+2) (1+3)(1+5)(1+8)
下面的也都可以这样同理获得。
显而易见如果想要知道他的父亲是谁,只要减去他能够减去的最大的斐波那契数,
就可以获得他的父亲,而每次只要对两个数大的进行操作,直到两个数相等,那
么相等的时候就是答案了。
#include <algorithm>
#include <cstdio>
#define lovelive long long
lovelive fib[100], n, a, b;
void swap(lovelive &a, lovelive &b) {
lovelive r = a;
a = b;
b = r;
}
void find(lovelive a, lovelive b) {
if (b > a) swap(a, b);
if (a == b) {
printf("%lld\n", a);
return;
}
int del = std::lower_bound(fib + 1, fib + 66, a) - fib;
find(a - fib[del - 1], b);
}
int main() {
fib[1] = 1;
fib[2] = 2;
for (int i = 3; fib[i - 1] < 1e15; i++) fib[i] = fib[i - 1] + fib[i - 2];
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &a, &b);
find(a, b);
}
}
7-11 I Say TingTing (30分)
这题原本还是很简单的,后来邪恶的学长突发奇想让每一个字符串用ting和gkd随机拼接,原本只有两种。
思想是看成一个斐波那契数列,对于每一个加入的ting有两种情况,和前面一个ting组成tingting的情况就是ans[n-1],和前面一个ting变成gkd的情况就是ans[n-2](因为前面少了一个ting)
然后用矩阵快速幂加速。
但因为前面可能保留了ting,所以每一串的第一遍需要额外判断一下和前面的ting的情况,后面的所有次数就都是一样的,也不要忘记把这次剩下的ting保留下来给下一个判断。
主要还是看代码吧,注释很清晰。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;
struct mat
{
ll a[2][2];
};
mat mat_mul(mat x, mat y)
{
mat res;
memset(res.a, 0, sizeof(res.a));
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++)
res.a[i][j] = (res.a[i][j] + x.a[i][k] * y.a[k][j]) % MOD;
return res;
}
ll mat_pow(ll n)
{
mat c, res;
c.a[0][0] = c.a[0][1] = c.a[1][0] = 1;
c.a[1][1] = 0;
memset(res.a, 0, sizeof(res.a));
for (int i = 0; i < 2; i++) res.a[i][i] = 1;
while (n)
{
if (n & 1) res = mat_mul(res, c);
c = mat_mul(c, c);
n = n >> 1;
}
return res.a[0][1];
}
ll fpow(ll n, ll k)
{
ll ans = 1;
while (k)
{
if(k&1)ans = ans * n % MOD;
k >>= 1;
n = n * n % MOD;
}
return ans;
}
int main()
{
int n;
cin >> n;
ll bf = 0;
ll ans = 1;
for (int i = 0; i < n; i++)
{
vector<ll> v;
ll ai;
string si;
cin >> ai >> si;
ll c = 0;
for (int i = 0; i < si.size();)
{
if (si[i] == 't')
{
i += 4;
c++;
}
else
{
i += 3;
v.push_back(c);
c = 0;
}
}
if (v.size() == 0)
{
//处理整个句子都没有gkd的情况,即整个句子都是ting留给下次做bf
bf += c * ai;
continue;
}
for (int i=0;i<v.size();i++)//计算第一次(可能跟前面剩余的ting有连接)
{
if(!i)ans = ans * mat_pow(v[i] + bf + 1) % MOD;
else ans = ans * mat_pow(v[i] + 1) % MOD;
}
//计算第2到ai次,每次之前有c个是上一次的
ll tmp = 1;
v[0] += c;
for (int i = 0; i < v.size(); i++)
{
tmp = tmp * mat_pow(v[i] + 1) % MOD;
}
ans = ans * fpow(tmp, ai - 1) % MOD;//ai-1次重复计算
bf = c;//置bf
}
ans = ans * mat_pow(bf + 1) % MOD;
cout << ans;
return 0;
}