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;
}
}