2020-2021JMU蓝桥校选题解


一直忘了写,现在补一下

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;
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值