2021JMU天梯校选部分题解

2021JMU天梯校选部分题解

7-1 一的个数 (5 分)

第一题签到题,直接搜过去就行,就不介绍了,这里放一个更加快的的办法,因为只要求1的个数,所以可以用上个数一的个数推出这个数的个数

	int n;
	cin >> n;
	a[0] = 0;
	for (int i = 1; i <= n; i++) {
		a[i] = a[i & (i - 1)] + 1;
		cout << a[i] << " ";
	}

i & (i - 1)这样得出的结果是把i中最后一位1去掉得出的数字(就是二进制中这个数1的个数比i少了一个),那么对于i这个数1的个数就是a[i & (i - 1)]+1

7-2 tly的生日 (5 分)

签到题,年份直接往后加,判断是不是闰年就行

7-3 括号匹配 (15 分)

整一个堆栈,依次读入符号,如果是左半边的直接放进去,如果是右半边就拿出堆栈第一个和他比较,是不是同一组的。全部判断完后,再看一看堆栈中是不是还有括号,如果有也是错误的

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int inf = 0x3f3f3f;
bool isValid(string s) {
    stack<char> st;
    int i;
    for (i = 0; i < s.size(); i++) {
        if (s[i] == '(' || s[i] == '[' || s[i] == '{') {
            st.push(s[i]);
        }
        else if (s[i] == ')') {
            if (st.empty()) return false;
            char ch = st.top();
            st.pop();
            if (ch != '(')return false;
        }
        else if (s[i] == '}') {
            if (st.empty()) return false;
            char ch = st.top();
            st.pop();
            if (ch != '{')return false;
        }
        else if (s[i] == ']') {
            if (st.empty()) return false;
            char ch = st.top();
            st.pop();
            if (ch != '[')return false;
        }
    }
    if (st.empty())return true;
    else return false;
}
int main() {
    string s;
    cin >> s;
    if (isValid(s))cout << "YES" << endl;
    else cout << "NO" << endl;
}

7-4 数名字 (15 分)

这题卡了一下读入的时间,我相信很多人都是超时(邪恶),读入的话,多次读入,每次读入一部分的时间会比一次读入全部慢,因为有一个打开读入,和关闭读入的动作。所以这题需要一次读入全部名字,然后一个个判断过去。

#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
#define ll long long 
const int inf = 0x3f3f3f;
#define qc std::ios::sync_with_stdio(0);
const int maxn = 5e7 + 10;
char ch[maxn];
int main() {
	//qc;
	//cin.tie(0);
	int n;
	scanf("%d",&n);
	getchar();
	scanf("%[^\n]", &ch);
	int len = strlen(ch);
	int cnt = 0;
	for (int i = 0; i < len; i++) {
		if (ch[i] != ' ' && (i == 0 || ch[i - 1] == ' '))cnt++;
		if (ch[i] == 'p' && ch[i + 1] == 'r' && ch[i + 2] == 'e' && ch[i + 3] == 'd' && ch[i + 4] == 'a' && ch[i + 5] == 't' && ch[i + 6] == 'o' && ch[i + 7] == 'r') {
			break;
		}
	}
	printf("%d",cnt);
}

7-5 弛神的心是冰冰的 (10 分)

这题观察题目,很容易发现颜色只有两种可能,要么1要么2,一棵树这样染色的话,一层一种颜色,最多只需要两种就可以。颜色为1的情况就是不存在边,都是单个点,没有相连的点就是1。然后就可以发现在下面输出的边中,只要出现左边不等于右边的,即存在一条边,那么就是2,否则就是1。需要注意的是题目可能出现森林的情况,没有说只有一棵树

#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
#define ll long long 
const int inf = 0x3f3f3f;
#define qc std::ios::sync_with_stdio(0);
signed main() {
	qc;
	cin.tie(0);
	ll n;
	cin >> n;
	int  flag = 1;
	for (int i = 0; i < n - 1; i++) {
		ll u, v;
		cin >> u >> v;
		if (u != c = v)flag = 2;
	}
	cout << flag;
}

7-9 鬼谷八荒 (20 分)

原本想写树的题目,后来写着写着变成了并查集,然后写标程的时候,写着写着又变成了像搜索…迷茫出了个四不像
最开始就是常规套路记录每一条边,记下两个人之间的联系,后面的解法有点像bfs,就是可能出现一个点出现好几次的可能,所以要用vis去标记出现过的人,越早出现,亲密度肯定越高。相比正常的bfs需要把每一层分开来标记,所以我往队列中放了-1作为标记,每到-1就知道当前层结束了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int inf = 0x3f3f3f;
#define qc ios::sync_with_stdio(0);
vector<int>a[100000 + 100];
bool vis[100000 + 100];
string s[100000 + 100];
int main() {
	qc;
	cin.tie(0);
	cout.tie(0);
	//ofstream outfile,infile;
	//infile.open("bbbb.in");
	//outfile.open("aaaa.out");
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		int x, y;
		cin >> x >> y;
		a[x].push_back(y);
		a[y].push_back(x);
	}
	vis[1] = true;
	int cnt = 1;
	queue<int>q;
	q.push(1);
	q.push(-1);
	priority_queue< int, vector<int>, greater<int>> pq;
	int k = 1;
	while (1) {
		int w;
		w = q.front();
		q.pop();
		if (w == -1) {
			while (pq.size()) {
				s[k] += to_string(pq.top());
				s[k] += " ";
				//cout << pq.top() << " ";
				pq.pop();
			}
			if (q.size() == 0)break;
			q.push(-1);
			k++;
		}
		for (auto it = a[w].begin(); it != a[w].end(); it++) {
			if (vis[*it] == true)continue;
			pq.push(*it);
			q.push(*it);
			cnt++;
			vis[*it] = true;
		}
	}
	if (cnt == n) {
		cout << "1 " << endl;
		for (int i = 1; i < k; i++)cout << s[i] << endl;
	}
	else cout << "-1 " << endl;
	//outfile.close();
	return 0;
}

7-13 拿金币 (20 分)

最简单的博弈论,其实这个游戏应该很多人小时候数学课就讲过
原本的样子是拿到最后一个获胜,这里改成了最后一个输。把金币数减一这题就可以当作拿最后一个赢得方法做了。因为拿了倒数第二个,就一定是赢,最后一个输得金币一定是对方拿。
上面分析可知题目变成了拿倒数第二个赢,首先我们n-1,然后每人每次最多拿m个金币,如果我是第二个拿的,我和对面两个人合起来一轮拿的个数我可以控制在m+1个。因为是我先手,所以我第一次拿走n%(m+1)个,剩下的我可以保证每组(m+1)个最后一个都是我拿的,也就是这n个金币的最后一个是我拿的。那什么时候作为先手的我必输,当我第一次没有金币可以拿的时候,n%(m+1)==0的时候,我和对手相当于顺序互换了,对手每次都可以和我组成m+1个,他将拿走最后一个。

#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
#define ll long long 
const int inf = 0x3f3f3f;
#define qc std::ios::sync_with_stdio(0);
int main() {
	qc;
	cin.tie(0);
	int t;
	cin >> t;
	while (t--) {
		ll n, m;
		cin >> n >> m;
		n--;
		if (n % (m + 1))cout << "tlyorz" << endl;
		else cout << "lzxorz" << endl;
	}
}

7-14 tly的巧克力 (20 分)

排列组合,题目表述很明确了,就是最后一个测试点卡了大数,求余的时候需要用到逆元,欧几里得求逆元,打表求也行。(发现有个人打表排列组合也过了,还是数据太弱了),逆元不会的可以去这里看一看
这里放个打表的答案

#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
#define ll long long 
const int inf = 0x3f3f3f;
#define qc std::ios::sync_with_stdio(0);
const int Maxn = 1e3 + 5, mod = 1e9 + 7;
int n, m;
int inv[Maxn] = { 0, 1 }, a[Maxn] = { 1, 1 }, b[Maxn] = { 1, 1 };
ll C(int x, int y)
{
	if (y == 0) return 1;
	if (x < 0 || y < 0 || y > x) return 0;
	return (ll)a[x] * b[y] % mod * b[x - y] % mod;//x!/(y!*(x-y)!)
}

void init()
{
	for (int i = 2; i <= 120; i++)
	{
		inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod;
		a[i] = (ll)a[i - 1] * i % mod;
		b[i] = (ll)b[i - 1] * inv[i] % mod;
	}
}

int main() {
	qc;
	cin.tie(0);
	int t;
	cin >> t;
	init();
	while (t--) {
		cin >> n >> m;
		cout << C(n,m) << endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值