2020JMU天梯校选题解

该题解为JMU-ACMer提供。让我们为善良的出题人点赞。谢谢!!!!

在这里插入图片描述
题解顺序可能跟比赛中不太一样,请通过标题进行查找

一、OrzLJL进行到底

这题大家应该是很熟悉了。毕竟每次校选都必膜一次。

需要注意的地方就是输出\的时候需要转移一下。(忘记转义符的,c语言可以回炉重造一下)。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>

char matrix[7][100] = {
    
    

"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\",
"\\\\   _        _ _      __   ____   ______  ____    \\\\",
"\\\\  | |      | | |     \\ \\ / /\\ \\ / /  _ \\/ ___|   \\\\",
"\\\\  | |   _  | | |      \\ V /  \\ V /| | | \\___ \\   \\\\",
"\\\\  | |__| |_| | |___    | |    | | | |_| |___) |  \\\\",
"\\\\  |_____\\___/|_____|   |_|    |_| |____/|____/   \\\\",
"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"

};
int main() {
    
    
	for (int i = 0; i < 7; i++) {
    
    
		printf("%s\n", matrix[i]);

	}
	return 0;
}

二、邪恶的TXT

这道就是个阅读理解题。需要根据这段英文写出这段答案。

首先The first three characters are "104". 所以前三个字符就是 104 了。

之后The next three characters are the lowercase of "TXT" 接下去三个字母是小写的txt。

最后the last fifteen characters are reversed with the string "neredeeixiuzihs" 最后十五个字符只需要把最后那个字符串倒转过来就是了。shizuixieederen

代码:

#include<stdio.h>

int main(){
    
    
    printf("104txtshizuixieederen");
    return 0;
}

三、H=1/2gt2

这题就是个高中物理。以下是某两位大佬考完说过的话。

在这里插入图片描述在这里插入图片描述

  • 符号定义

    h0:桌子的高度

    h1:纸板的高度

    x:纸板到桌子的水平距离

    y:衣服到桌子的水平距离

  • 考虑一种情况:h0<h1

    纸板高于桌子,永远无法到达。

  • 第二种情况: h0 >h1

    • 桌子高于纸板,小球做平抛运动,此时弹珠能到达地面的最近距离和纸板之间形成了一个盲区,

      此处是无法到达的,输出 xie’e

      在这里插入图片描述

代码:

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int main() {
    
    
    double x, h0, h1;
    double y;
    cin >> h0 >> h1 >> x >> y;
    assert(h0 > 0 && h1 > 0 && x > 0 && y > 0);
    double t = sqrt(2.0 * h0 / 10.0);
    double dh = h0 - h1;
    if (dh < 0 && y > x) cout << "xie'e";
    else {
    
    
        double t1 = sqrt(2 * dh / 10);
        double vx = x / t1;
        double t2 = sqrt(2 * h0 / 10);
        double right_bound = t2 * vx;
        if (y > x && y < right_bound) cout << "xie'e";
        else {
    
    
            double v = y / t2;
            printf("%.2f", v);
        }
    }
    return 0;
}

四、勋总的课1

这题是要输出整数的二进制形式。如果你学过源码、补码、位运算。那么这题是很容易做出来的。如果还未听过或者学习过的话。建议先去学下。

#include<iostream>
using namespace std;
//-------------------------------------
int main(){
    
    
  for(int n; cin>>n; ){
    
    
    for(int i=31; i>=0; --i)
      {
    
    
 	  	  cout<<(n>>i & 1);
	      if(i%8==0 && i!=31) cout<<' ';
      }
    cout<<"<->"<<n<<"\n";
  }
}

五、谁是龙王

要找出谁的发言次数最多。如果发言次数最多的有多个人,那么就选则最先发言的人为龙王。

这题如果会STL的话写起来是很轻松的。用过map记录每个人的发言数,还有一个map记录每个人第一次发言时间点。 然后遍历map找出发言数最多的。接着找出发言次数最多的人中,第一次出现的。map的插入和查找时间都是logn的,所以用这种方法整体时间复杂度为O(nlogn)。

纯C写发的话,可以定义一个结构体,这个结构体里面存放一个字符串,用于保存名字,还有一个idx遍历用于保存第一次出现的位置,count遍历用于保存出现的次数。

之后定义一个该结构体数组,每次输入一个人名后,先遍历数组,判断当前输入的人名是否在数组中了,如果有,则出现次数+1。若没有,则加到数组中。

因为题面保证人名的种类不超过200个,发言条数n不过超1e5。所以整体复杂度为O( 200n)。也才1e7。不会发生TLE。

struct node{
    
    
    char name[100];
    int count,idx;
}

代码:

#include <bits/stdc++.h>
using namespace std;
int main() {
    
    
    //freopen("../tianti/2.in", "r", stdin);
   // freopen("../tianti/2.out", "w", stdout);
    int n;
    cin >> n;
    string a;
    int i;
    unordered_map<string, int> mmid, map;
    for (i = 0; i < n; i++) {
    
    
        cin >> a;
        mmid[a]++;
        if (!map[a]) map[a] = 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;
}

六、暴风雨前的宁静

对于n<=3的情况,我们不难特判枚举出来,当n>3时,我们可以按照先把1-n中的偶数输出,然后再输出1-n中的奇数。n为奇数时:2,4,6,…n-1,1,3,5,…,n,因为除了n和2这个位置其他位置的相邻奇偶性相同,必然满足差值>1,因为n>3且n为奇数——即n>=5,所以n-2>1的;n为奇数时:2,4,6,…n-1,1,3,5,…,n,同理可证其满足题目要求。

#include <stdio.h>
int main()
{
    
    
	 int h, i, j, n;
	 scanf("%d", &n);
	 if(n==1||n==2)
	 {
    
    
		 printf("1\n1");
	 }
	 else if(n==3)
	 {
    
    
		 printf("2\n1 3");
	 }
	 else if(n==4)
	 {
    
    
		 printf("4\n2 4 1 3");
	 }
	 else
	 {
    
    
		  printf("%d\n1", n);
		  for(i=3;i<=n;i+=2)
		  {
    
    
			  printf(" %d", i);
		  }
		  for(i=2;i<=n;i+=2)
		  {
    
    
			  printf(" %d", i);
		  }
	 }
	 return 0;
}

七、背靠背

这题就是找规律输出了。

通过观察可以看到,就单独一行来看,中间被一个空格分成左右两边,而且是堆成的,但末尾是没有空格的。

然后我们先单单只看左边,每行字符串的个数是逐渐加1的。前导空格是逐渐减一的。

还有注意多组输入之间读入空白字符的问题。代码如下

#include<stdio.h>
int main()
{
    
    
	char ch;
	int i, n, j, temp,h,k;
	while (~scanf(" %c %d", &ch, &n))
	{
    
    
		temp = n;
		for (i = 1; i <= n; i++)//控制行数
		{
    
    
			for (j = 1; j < temp; j++)//每行空格数
				printf(" ");
			for (j = 0; j < i; j++)
				printf("%c", ch);
			printf(" ");
			for (j = 0; j < i; j++)
				printf("%c", ch);
			printf("\n");
			temp--;
		}
	}
	return 0;
}

八、Nobody knows code better than me

这题是道模拟题,只要你会任意一种排序算法,就能解出来。

首先得记录每个人的编号,还有成绩,然后我们以成绩为关键字,按照从大到小的顺序排个序。

排好序之后,就可以给每个人排名了。题面要求如果有多个成绩相同的人,他们的排名是一样的,但是后面的人的排名计数依然会增加

就比如说分数为99 99 98,那么这三个分数对应的排名就位 1 1 3

所以根据上述规则,在拍好序后给每个人排名。在拍完名之后,再以编号为关键字,从小到大排序一次。

具体实现代码如下:

/**
 * Created by jiangxiaoju on 2020/10/19 20:50.
 */
#include <bits/stdc++.h>

using namespace std;

#define mp(a, b) make_pair(a, b)
#define mid ((l + r) >> 1)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r

typedef long long ll;
typedef pair<ll, int> pli;
typedef pair<int, int> pii;
typedef pair<int, double> pid;
typedef pair<double, double> pdd;
typedef pair<double, int> pdi;
typedef pair<ll, double> pld;
typedef pair<double, ll> pdl;
typedef pair<ll, ll> pll;
const long long INF = 1e15;
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;

template<class T>
bool isPrime(T x) {
    
    
    if (x <= 1)
        return false;
    if (x == 2)
        return true;
    if (x % 2 == 0)
        return false;
    T m = sqrt(x);
    for (T i = 3; i <= m; i += 2) {
    
    
        if (x % i == 0)
            return false;
    }
    return true;
}

template<class T>
T gcd(T m, T n) {
    
     return m % n == 0 ? n : gcd(n, m % n); }


ll qpow(ll x, ll y) {
    
    
    ll res = 1;
    while (y) {
    
    
        if (y & 1)
            res = res * x;
        x = x * x;
        y >>= 1;
    }
    return res;
}

template<class T>
ll qpow(ll x, ll y, T mod) {
    
    
    ll res = 1;
    while (y) {
    
    
        if (y & 1)
            res = res * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}

struct node {
    
    
    ll scores, idx, num;
    node(ll a, ll b, ll c = 0) : scores(a), num(b), idx(c) {
    
    }
};

int main() {
    
    
    ios::sync_with_stdio(false);
    int n;
    cin >> n;
    ll scores, num;
    int i;
    vector<node> v;
    for (i = 0; i < n; i++) {
    
    
        cin >> num >> scores;
        v.push_back(node(scores, num));
    }

    sort(v.begin(), v.end(), [&](const node &n1, const node &n2) {
    
    
        return n1.scores > n2.scores;
    });

    ll cnt = 1;
    for (i = 0; i < n; i++) {
    
    
        if (i == 0) {
    
    
            v[i].idx = cnt;
        } else {
    
    
            if (v[i].scores != v[i - 1].scores) {
    
    
                v[i].idx = ++cnt;
            } else {
    
    
                int tmp = 0;
                while (i < n && v[i].scores == v[i - 1].scores) {
    
    
                    v[i].idx = cnt;
                    i++;
                    tmp++;
                }
                i--;
                cnt += tmp;
            }
        }
    }

    sort(v.begin(), v.end(), [&](const node &n1, const node &n2) {
    
    
        return n1.num < n2.num;
    });
    for(auto&val:v){
    
    
        cout<<val.idx<<endl;
    }
    return 0;
}

如果觉得上面这种写法太麻烦,还有个跟简洁的做法。

因为n<=5000,所以n^2 能过,不需要node、pair之类的排序,因为编号是n的一个排列,所以也不需要map、离散化处理过大的情况,直接数组存储即可。这里只给出最简便的做法:O(n^2),记ans[i]为第i个人排名初始化为1,先把每一个编号的分数按下标存储进对应的数组中,然后先遍历数组枚举每一个人的分数ai,对于枚举的a,再遍历数组枚举每一个人的分数aj,如果aj>ai,就说明i这个人的排名要后退一位,因为j的成绩比他高,所以ans[i]++;最后输出按序输出ans即可。

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define  rd read()
#define pi 3.1415926535
using namespace std;
const ll mod = 998244353;
const int MAX1 = 100005;
const int MAX2 = 300005;
inline ll read() {
    
    
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch>'9') {
    
    
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
    
    
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
ll fpow(ll a, ll b)
{
    
    
    ll ans = 1;
    while (b)
    {
    
    
        if (b & 1)ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
ll gcd(ll a, ll b) {
    
     return !b ? a : gcd(b, a % b); }
ll b[10000];
ll ans[10000];
int main()
{
    
    

    int n = rd;
    int i;
    for (i = 0; i < n; i++)
    {
    
    
        ll t = rd;
        b[t] = rd;
        ans[t] = 1;//初始化每个人的分数
    }
    for (i = 1; i <= n; i++)
    {
    
    
        for (int j = 1; j <= n; j++)
        {
    
    
            if (b[j] > b[i])ans[i]++;
        }
    }
    for (i = 1; i <= n; i++)cout << ans[i] << endl;
}

九、从零开始的良心出题人招募

题目要求各个位数的平方和要=其对n取模的结果,所以可以知道每个位数都是要<=sqrt(n-1),因为n<=25,所以sqrt(n-1)<=4,因此每一位数都<=4,所以dfs搜索m个数(第一位数从1到4,之后每一位数从0到4,因为不能有前导零),复杂度O(4^m)。当然也可以直接暴力搜索m个数(第一位数从1到9,之后每一位数从0到9),然后剪枝一下——当当前的各个位数的平方和>=n的时候就直接return即可。

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define  rd read()
#define pi 3.1415926535
using namespace std;
const ll mod = 998244353;
const int MAX1 = 100005;
const int MAX2 = 300005;
inline ll read() {
    
    
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch>'9') {
    
    
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
    
    
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
ll fpow(ll a, ll b)
{
    
    
    ll ans = 1;
    while (b)
    {
    
    
        if (b & 1)ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
ll gcd(ll a, ll b) {
    
     return !b ? a : gcd(b, a % b); }
ll ans = 0;
int n;
int m;
ll k;
int f;
void dfs(int i, int sum, ll num)
{
    
    
    if (i == m)
    {
    
    
        if (num % n == sum)
        {
    
    
            cout << num % k << endl;
            f = 1;
        }
        return;
    }
    if (i)
    {
    
    
        for (int j = 0; j <= sqrt(n - 1); j++)
        {
    
    
            dfs(i + 1, sum + j * j, num * 10 + j);
        }
    }
    else
    {
    
    
        for (int j = 1; j <= sqrt(n - 1); j++)
        {
    
    
            dfs(i + 1, sum + j * j, num * 10 + j);
        }
    }
}
int main()
{
    
    
    m = rd, n = rd, k = rd;
    dfs(0, 0, 0);
    if (!f)cout << -1;
    return 0;
}

十、勋总的课2

给一个表达式,要要判该表达式是否满足r进制下的运算结果,题面保证2<=r<=16。并且表达式中的数的值为非负数。这题最简单的方法就是用Java写了。

先介绍用Java的写法:按运算符更好完字符串后,暴力判断r从2到16的情况。因为数据可能超过2的31次方。所以用long类型的保存每个数。 在把字符串转成数值类型是,可以用Long提供的方法Long.valueOf(strings[i], radix); 记得try cathe一下异常。具体实现可以参考第一份代码。

c\c++做法,其实也跟Java的写法差不多。也是先分割字符串。然后把字符串转成数值后进行判断,只不过比较麻烦的是得自己实现字符串转成R进制下的整数的。 具体实现过程可以参考第二份代码。

Java:

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;

public class Main {
    
    
    public static Long[] change(String[] strings, int radix) {
    
    
        Long[] arr = new Long[strings.length];
        for (int i = 0; i < strings.length; i++) {
    
    
            arr[i] = Long.valueOf(strings[i], radix);
        }
        return arr;
    }
    public static void main(String[] args) {
    
    
        Scanner in = new Scanner(System.in);
        String str;
        while (in.hasNext()) {
    
    
            str = in.nextLine();
            String[] split = str.split("[+|*|/|=|-]");
            List<String> ops = new ArrayList<>();
            for (int i = 0; i < str.length(); i++) {
    
    
                if (str.charAt(i) == '+' || str.charAt(i) == '-' || str.charAt(i) == '*' || str.charAt(i) == '/') {
    
    
                    ops.add(str.charAt(i) + "");
                }
            }
            int r;
            boolean flag = false;
            for (r = 2; r <= 16; r++) {
    
    
                if (flag) break;
                Long[] arr = new Long[20];
                try {
    
    
                    arr = change(split, r);
                } catch (Exception e) {
    
    
                    continue;
                }
                Long ans = arr[0];
                int j = 0;
                boolean tmp = true;
                for (int i = 1; i < arr.length - 1; i++) {
    
    
                    Long num = arr[i];
                    String op = ops.get(j++);
                    if (op.equals("+")) {
    
    
                        ans += num;
                    } else if (op.equals("-")) {
    
    
                        ans -= num;

                    } else if (op.equals("/")) {
    
    
                        if (ans % num != 0) {
    
    
                            tmp = false;
                            break;
                        }
                        ans /= num;
                    } else if (op.equals("*")) {
    
    
                        ans *= num;
                    }
                }
                if (tmp && ans.equals(arr[arr.length - 1])) {
    
    
                    flag = true;
                    break;
                }

            }
            if (flag) {
    
    
                System.out.println(r);
            } else {
    
    
                System.out.println(-1);
            }
        }
    }
}

c++:

#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
enum op_t{
    
    EMPTY, ADD, DEC, MUL, DIV};
int get(char ch) {
    
    
	int number = -1;
	if (ch >= '0' && ch <= '9') number = ch - '0';
	else if (ch >= 'a' && ch <= 'z')  number = 10 + ch - 'a';
	else if (ch >= 'A' && ch <= 'Z')  number = 10 + ch - 'A';
	return number;
}
unsigned long long get(const string& str, int radix) {
    
    
	unsigned long long p = 1;
	unsigned long long res = 0;
	for (int i = str.size() - 1; i >= 0; i--) {
    
    
		int n = get(str[i]);
		if (n >= radix) return -1;
		res += n * p;
		p *= radix;
	}
	return res;
}
bool check(string& s1, op_t op, string& s2, string& ans, int radix) {
    
    
	unsigned long long _n1 = 0, _n2 = 0, _n3 = 0;
	
	_n1 = get(s1, radix);
	_n2 = get(s2, radix);
	_n3 = get(ans, radix);
	if (_n1 == -1 || _n2 == -1 || _n3 == -1) return false;
	switch (op) {
    
    
	case ADD:
		return _n1 + _n2 == _n3;
	case DEC:
		return _n1 - _n2 == _n3;
	case MUL:
		return _n1 * _n2 == _n3;
	case DIV:
		return _n2 * _n3 == _n1;
	default:
		return false;
	}
}
int main() {
    
    
	string str;
	while (cin >> str) {
    
    
		op_t op = EMPTY;
		string s1, s2, ans;
		int op_idx = -1;
		int eq_idx = -1;
		int idx = 0;
		for (char ch : str) {
    
    
			int number = -1;
			if (ch >= '0' && ch <= '9') number = ch - '0';
			else if (ch >= 'a' && ch <= 'z')  number = 10 + ch - 'a';
			else if (ch >= 'A' && ch <= 'Z')  number = 10 + ch - 'A';
			else if (ch == '+') {
    
    
				op = ADD;
				op_idx = idx;
			}
			else if (ch == '-') {
    
    
				op = DEC;
				op_idx = idx;
			}
			else if (ch == '*') {
    
    
				op = MUL;
				op_idx = idx;
			}
			else if (ch == '/') {
    
    
				op = DIV;
				op_idx = idx;
			}
			else if (ch == '=') {
    
    
				eq_idx = idx;
			}
			
			idx++;
		}
		s1 = string(str.begin(), str.begin() + op_idx);
		s2 = string(str.begin() + op_idx + 1, str.begin() + eq_idx);
		ans = string(str.begin() + eq_idx + 1, str.end());
		//cout << s1 << ' ' << s2 << ' ' << ans << endl;
		int radix;
		for (radix = 2; radix <= 16; radix++) {
    
    
			if (check(s1, op, s2, ans, radix)) {
    
    
				printf("%d\n", radix);
				break;
			}
		}
		if (radix >= 17) printf("-1\n");
	}
	return 0;
}

十一、SICP熵

这题题意是要求一个序列中逆序对的个数。

设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。

如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。

求逆序对的个数做法有很多,权值线段树、树状数组、归并排序都是可以的。

权值线段树的做法,首先得学过线段树 :POJ2299 Ultra-QuickSort 权值线段树求逆序对 详细图解,用全职线段树求解的方法,可以参考这篇博客,附有详细的图解过程。

这里说下最简单的归并排序的做法,只需要在归并排序的Merge函数加句话即可。ans += (midIndex + 1 - i);

因为归并左右两个数组是已经排序好的,这时候第j个数要进来,说明左半部分(start到mid)部分中剩余的个数都是比第j个数大的,但是他们又在j前面,所以ans+=左半部分(start到mid)部分中剩余的个数。

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define  rd read()
#define pi 3.1415926535
using namespace std;
const ll mod = 998244353;
const int MAX1 = 100005;
const int MAX2 = 300005;
inline ll read() {
    
    
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch>'9') {
    
    
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
    
    
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
ll fpow(ll a, ll b)
{
    
    
    ll ans = 1;
    while (b)
    {
    
    
        if (b & 1)ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
ll gcd(ll a, ll b) {
    
     return !b ? a : gcd(b, a % b); }
long long ans = 0;
void Merge(int sourceArr[], int tempArr[], int startIndex, int midIndex, int endIndex)
{
    
    
    int i = startIndex, j = midIndex + 1, k = startIndex;
    while (i != midIndex + 1 && j != endIndex + 1)
    {
    
    
        if (sourceArr[i] > sourceArr[j])
        {
    
    
            tempArr[k++] = sourceArr[j++];
            //cout <<i<<' '<<j<<' '<< startIndex<<' '<< midIndex<<' '<< endIndex<<' '<<(midIndex + 1 - i) << endl;
            ans += (midIndex + 1 - i);
        }
        else
            tempArr[k++] = sourceArr[i++];
    }
    while (i != midIndex + 1)
        tempArr[k++] = sourceArr[i++];
    while (j != endIndex + 1)
        tempArr[k++] = sourceArr[j++];
    for (i = startIndex; i <= endIndex; i++)
        sourceArr[i] = tempArr[i];
}

void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex)
{
    
    
    ll midIndex;
    if (startIndex < endIndex)
    {
    
    
        midIndex = (startIndex + endIndex) / 2;
        MergeSort(sourceArr, tempArr, startIndex, midIndex);
        MergeSort(sourceArr, tempArr, midIndex + 1, endIndex);
        Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);
    }
}
int a[1000005], b[1000005];

int main()
{
    
    
    int i;
    int n = rd;
    for (int i = 0; i < n; i++)a[i] = rd;
    MergeSort(a, b, 0, n - 1);
    cout << ans << endl;
    return 0;
}

十二、汤氏集团

这题题意是要求无序数组内,任意区间的第k大数的模板题。

可以用可持久化线段树、划分树去求解。由于这两部分内容太多,就请大家自己网上搜教程了吧。

这里附上可持久化线段树的代码。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 2e5 + 10;
struct Node {
    
    
    int l, r, sum;
} T[maxn * 40];

int root[maxn], arr[maxn], n, m, cnt = 0;
vector<int> v;
int getid(int x) {
    
    

    return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}

void update(int l, int r, int &x, int y, int pos) {
    
    
    T[++cnt] = T[y];
    x = cnt;
    T[x].sum++;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) update(l, mid, T[x].l, T[x].l, pos);
    else
        update(mid + 1, r, T[x].r, T[x].r, pos);
}

int query(int l, int r, int x, int y, int k) {
    
    
    if (l == r) return l;
    int sum = T[T[y].r].sum - T[T[x].r].sum;
    int mid = (l + r) >> 1;
    if (sum >= k) return query(mid + 1, r, T[x].r, T[y].r, k);
    else
        return query(l, mid, T[x].l, T[y].l, k - sum);
}


int main() {
    
    
    ios::sync_with_stdio(false);
    cin >> n >> m;
    int i;
    for (i = 1; i <= n; i++) {
    
    
        cin >> arr[i];
        v.push_back(arr[i]);
    }
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    for (i = 1; i <= n; i++) {
    
    
        update(1, n, root[i], root[i - 1], getid(arr[i]));
    }
    int a, b, k;
    for (i = 1; i <= m; i++) {
    
    
        cin >> a >> b >> k;
        cout << v[query(1, n, root[a - 1], root[b], k) - 1] << endl;
    }
    return 0;
}

十三、TXT斩向日葵

在这里插入图片描述

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 100010, M = 200010;
int Case, n, m, i, j, x, y, a[N], q[N], g[N], v[M << 1], nxt[M << 1], ed, f[N], fa[N], wake[N]; long long ans;
inline void add(int x, int y) {
    
     v[++ed] = y; nxt[ed] = g[x]; g[x] = ed; }
inline bool cmp(int x, int y) {
    
     return a[x] > a[y]; }
int F(int x) {
    
     return f[x] == x ? x : f[x] = F(f[x]); }
int main() {
    
    
    scanf("%d%d", &n, &m);
    for (ed = 0, i = 1; i <= n; i++) {
    
    
        scanf("%d", &a[i]);
        q[i] = f[i] = i;
        g[i] = fa[i] = wake[i] = 0;
    }
    while (m--)scanf("%d%d", &x, &y), add(x, y), add(y, x);
    sort(q + 1, q + n + 1, cmp);
    for (i = 1; i <= n; i++) {
    
    
        x = q[i];
        wake[x] = 1;
        for (j = g[x]; j; j = nxt[j]) {
    
    
            y = v[j];
            if (!wake[y])continue;
            y = F(y);
            if (y == x)continue;
            fa[y] = f[y] = x;
        }
    }
    for (ans = 0, i = 1; i <= n; i++)ans += a[i] - a[fa[i]];
    printf("%lld\n", ans);
}

十四、廉讯公司

题意:

转换一个128位的二进制串,变成16进制,并且格式为x : x : x : x : x : x : x : x然后多个相邻的0变成::,且只有一个::,求变成的字典序最小且最短的字符串。然后根据输入的字符串求最早出现时间.

思路:

先将二进制转化为十进制,然后用%x输出,因为连续的0可变短,但有优先级,首先选0长的,然后如果长度相等,先选中间,中间里面先选中间偏后,然后是最后,最后是前面。然后时间用map记录一下,这里注意时间最好转成秒,方便以小到大输出.

#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <string>
using namespace std;
int main()
{
    
    
    int n, H, M, S, t;
    int a[10], r[10];
    char s[150],tmp[150];
    map<string, int> mp;
    string str;
    scanf("%d", &n);
    for (int cas = 1; cas <= n; cas++)
    {
    
    
        scanf("%d:%d:%d--->%s",&H,&M,&S, s + 1);
        t = H * 3600 + M * 60 + S;
        for (int i = 1; i <= 8; i++)
        {
    
    
            a[i] = 0;
            for (int j = 1; j <= 16; j++)
                a[i] = a[i] * 2 + s[(i - 1) * 16 + j] - '0';
        }
        memset(r, 0, sizeof(r));
        int id = 0;
        for (int i = 8; i >= 1; i--)
        {
    
    
            if (a[i] == 0) r[i] = r[i + 1] + 1;
            if (r[id] < r[i]) id = i;
            if (r[id] == r[i] && r[id] + id - 1 == 8 && i != 1) id = i;
        }
        str.clear();
        for (int i = 1; i <= 8; i++)
        {
    
    
            if (i == id && r[i] >= 2)
            {
    
    
                if (id == 1) str.append(":");
                str.append(":");
                i = i + r[i] - 1;
            }
            else
            {
    
    
                sprintf(tmp,"%x", a[i]);
                str.append(tmp);
                if (i != 8) str.append(":");
            }
        }
        if(!mp[str])mp[str] = t;
        cout << str << endl;
    }
    scanf("%s", s);
    str = s;
    t = mp[str];
    cout << t/3600 << ":" << (t%3600)/60 << ":" << t%60 << endl;
}

十五、LFU

做法: 利用稀疏链表(双向)来解决存储结构问题
如果不用哈希表存<key, (行指针, 列指针)>, 就遍历所有链表找<key,value>
使用哈希表存<key, (行指针, 列指针)>, 可以直接O(1)查找值,然后进行链表节点转移

考虑这几个点

加入一个新点(set一个新的key)
  • 如果链表不存在,则直接创建
  • 如果链表存在, 且第一个链表代表次数1, 则添加至末端
  • 如果链表存在, 但第一个链表不代表次数1, 则添加新的链表代表次数1
  • 对于哈希表修改:添加<key, (行指针, 列指针)>
覆盖一个旧点(set一个已存在的key)
  • 假设此时当前key所在节点为次数x的链表, 将其转移至次数为x+1的链表
  • 如果下一个链表表示x+1, 直接将其接至末尾
  • 如果下一个链表不表示x+1, 或者无链表, 新建代表x+1的链表
  • 对于哈希表修改:修改原来的行指针
获得一个点的值(get一个已存在的key)
  • 不存在不加点
  • 存在, 同上,此时在x链表, 将其转移至x+1的链表
  • 对于哈希表修改:修改原来的行指针

set TXT ORZ

new
1
LJL, ORZ
TXT,ORZ (new)
2
IP, 114.114.114.114

set LT CAI

new
new
delete
delete
1
LT, CAI (new)
TXT,ORZ
LJL, ORZ(delete)
2
IP, 114.114.114.114

set TXT TQL

delete
new
1
LT, CAI
TXT, ORZ
2
IP, 114.114.114.114

get TXT

delete
new
2
IP,114.114.114.114
LJL,TQL
TXT, TQL
3(new)

考虑此样例

输入:

3
set 1 1
set 2 2
set 3 3
set 2 22
get 1
get 2
get 3

输出:

1
22
3

考虑其中set 2 22

delete
delete
new
new
1,1
2,2
3,3
1
2

参考代码(C++警告)

关于unodered_map的重载 自己百度一下呗

#include<unordered_map>
#include<iostream>
#include<vector>
#include<list>
#include<iterator>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
using namespace std;

	

template<class key_t, class val_t, class Hasher = hash<key_t>>
class LFU_Cache {
    
    
private:
	size_t capacity;
	size_t left_size;

	using pkv_t = pair<key_t, val_t>;
	using list_pkv_t = list<pkv_t>;
	list< pair<size_t, list_pkv_t> >cache_list;

	using row_iter_t = typename decltype(cache_list)::iterator;
	using col_iter_t = typename list_pkv_t::iterator;
	unordered_map<key_t, pair<row_iter_t, col_iter_t>, Hasher>mp;
public:
	LFU_Cache(size_t capacity) :capacity(capacity) {
    
    
		left_size = capacity;
	}

	/*
	* get
	* @return: {false, ...} represent not found
	*		   {true,  ...} represent found
	 */
	pair<bool, val_t> get(const key_t &key) {
    
    
		auto p = mp.find(key);
		if (p == mp.end()) {
    
    
			return {
    
     false, key_t() };
		}
		else {
    
    
			p->second = to_next_times_list(p->second.first, p->second.second);
			return {
    
     true, (p->second.second)->second };
		}
	}
	/*
	* set
	* @param key
	* @param val
	*
	*/
	void set(const key_t &key, const val_t &val) {
    
    
		auto p = mp.find(key);
		if (p == mp.end()) {
    
     //not found
			if (!left_size) remove();
			auto row_iter = cache_list.begin();
			if (row_iter == cache_list.end() || row_iter->first != 1) {
    
    
				row_iter = cache_list.insert(row_iter, {
    
     1, {
    
    } });
			}
			list_pkv_t& col_list = row_iter->second;
			auto col_iter = col_list.insert(col_list.end(), {
    
     key, val });
			mp[key] = {
    
     row_iter, col_iter };
			left_size--;
		}
		else {
    
     //found
			auto res = to_next_times_list(p->second.first, p->second.second);
			p->second = res;
			(p->second.second)->second = val;
		}
	}
private:
	/*
	* move current node to next times list,
	* {x, y} -> {x + 1, last one}
	*/
	pair<row_iter_t, col_iter_t> to_next_times_list(row_iter_t row_iter, col_iter_t col_iter) {
    
    

		list_pkv_t& col_list = row_iter->second;

		//remove it to a new list
		{
    
    
			//remove
			pkv_t _pkv = *col_iter; //save key, value
			size_t rep_op = row_iter->first; // save the repeat time
			col_list.erase(col_iter);
			//insert to a new list
			if (col_list.size() == 0) 
				row_iter = cache_list.erase(row_iter);
			else 
				row_iter++;

			//not a corresponding list, create and redirect to a new list
			if (row_iter == cache_list.end() || row_iter->first != rep_op + 1) {
    
    
				row_iter = cache_list.insert(row_iter, {
    
     rep_op + 1, {
    
    } });
			}
			list_pkv_t& col_list = row_iter->second;
			//push back
			col_iter = col_list.insert(col_list.end(), _pkv);
		}
		
		return {
    
     row_iter, col_iter };
	}
	/*
	*	remove the first cache
	*/
	void remove() {
    
    
		//no left
		if (capacity == left_size) return;

		auto row_iter = cache_list.begin();
		list_pkv_t& col_list = row_iter->second;
		auto col_iter = col_list.begin();
		key_t key = col_iter->first;
		mp.erase(key);
		col_list.erase(col_iter);
		if (col_list.size() == 0) cache_list.erase(row_iter);

		left_size++;
	}

};

struct string_hash {
    
    
	static const size_t BASE = 131;
	size_t operator() (const string& s) noexcept {
    
    
		size_t res = 0;
		for (auto it = s.begin(); it != s.end(); it++) {
    
    
			res = res * BASE + *it;
		}
		return res;
	}
};
int main() {
    
    
	//freopen("C:\\Users\\Administrator\\Desktop\\data\\3.in", "r", stdin);
	//freopen("C:\\Users\\Administrator\\Desktop\\data\\3.out", "w", stdout);
	int size;
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> size;
	LFU_Cache<string, string, hash<string>>lfu(size);
	string op, s1, s2;
	while (cin >> op) {
    
    

		if (op == "get") {
    
    
			cin >> s1;
			auto p = lfu.get(s1);
			if (p.first)
				cout << p.second << endl;
			else cout << "Not Found\n";
		}
		else if (op == "set") {
    
    
			cin >> s1 >> s2;
			lfu.set(s1, s2);
		}

	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43058685/article/details/109213745