牛客练习赛133
A 万年沉睡的宝藏
题意:有一些岛和一些宝藏,都用字符串来描述,会有4个操作:给一个岛加一个宝藏,问这个岛有多少宝藏,某个宝藏是否在这个岛上,有多少岛上有至少一个宝藏。
用map存string和set
点击查看代码
void solve() {
int q;
std::cin >> q;
std::map<std::string, std::set<std::string> > mp;
while (q -- ) {
int op;
std::cin >> op;
if (op == 1) {
std::string s, t;
std::cin >> s >> t;
mp[s].insert(t);
} else if (op == 2) {
std::string s;
std::cin >> s;
int ans = mp.count(s) ? mp[s].size() : 0;
std::cout << ans << "\n";
} else if (op == 3) {
std::string s, t;
std::cin >> s >> t;
int ans = mp.count(s) ? mp[s].count(t) : 0;
std::cout << ans << "\n";
} else {
std::cout << mp.size() << "\n";
}
}
}
B 完美主义追求者
题意:给你a和b,求a个点的二叉树有多少个本质不同的,对b取模。
猜了一下是a的阶乘个,不会证明。
因为a很大,无法算阶乘,但注意b只有1e6,当a<b时暴力,否则a>=b时因为因子乘了一个b,模数肯定是0.
(赛时写的python,以为要用高精度算阶乘,结果wa了一发,后来发现正确做法后只需要随便改一下就行了,所以还是python代码)
点击查看代码
import math
a, b = map(int, input().split());
if a < b:
ans = 1
for i in range(1, a + 1):
ans = ans * i % b
print(ans)
else:
print(0)
C 异或与位移
题意:一个长度位n的数组a,和一个k,a的值在-k + 1到 k - 1之间,m个询问,给你一个二进制数y,它是由一个x变过来的:第i次变化中,如果
因为我们知道y,所以我们可以反过来操作,举个例子,对于一个二进制数abc(a, b, c等于0或1), 它左移2位等于110,那么就是c00 ^ abc = 110这是可以求出来abc的,先根据那几个0来确定后面几位,然后根据后面的数确定前面的数。右移一样的思路。 那么我们可以倒退操作找到x。
点击查看代码
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
while (m -- ) {
std::string s;
std::cin >> s;
s = std::string(k - (int)s.size(), '0') + s;
bool flag = true;
for (int i = n - 1; i >= 0; -- i) {
std::string t(k, '?');
if (a[i] > 0) {
for (int j = k - a[i]; j < k; ++ j) {
t[j] = s[j];
}
for (int j = k - a[i] - 1; j >= 0; -- j) {
if (t[j + a[i]] == '?') {
flag = false;
break;
}
t[j] = ((t[j + a[i]] ^ s[j]) & 1) + '0';
}
} else {
for (int j = 0; j < -a[i]; ++ j) {
t[j] = s[j];
}
for (int j = -a[i]; j < k; ++ j) {
if (t[j + a[i]] == '?') {
flag = false;
break;
}
t[j] = ((t[j + a[i]] ^ s[j]) & 1) + '0';
}
}
if (t.find('?') != t.npos) {
flag = false;
}
if (!flag) {
break;
}
s = t;
// std::cout << s << "\n";
}
if (!flag) {
std::cout << -1 << "\n";
continue;
}
int p = s.find('1');
if (p == s.npos) {
std::cout << "0\n";
} else {
std::cout << s.substr(p) << "\n";
}
}
}
D 被拒绝在外的打卡
写麻了,每次wa一发后就发现了问题,前前后后de一个小时bug,最后还是没过,感觉应该还是思路的问题。
题意:给你一棵树或者一棵基环树,开始每个点上都有人,每一时刻每个点上的人都同时移动,两个相邻点上的人可以互换位置,问至少要加几个和原有点连接使得可以无限移动下去。如果不需要加点就输出Yes。
看了官方题解后发现思路和解法2差不多,只不过我没想到基环树不要特殊处理,结果自己特判基环树情况搞个基环树dp搞半天没搞出来。
一个点要么在环里面围着这个环跑,要么和一个相邻的点轮流换。那么考虑树的情况,我们从叶子节点开始匹配,因为叶子节点的匹配是固定的,只能和它的父亲匹配。每个点搞完后会有新的叶子,这样搞下去发现某些点没有可以匹配的就ans+1。那么对于基环树的情况为什么也有效,把官方题解贴这里:
链接:https://ac.nowcoder.com/discuss/1450957
对于基环树,我们有两种情况:
-
环上的点轮着移动
这种情况通过上述方式能够匹配出来。
-
有一条边不会被匹配
我们按照上述方式匹配直到环上的一个点,此时将这个点匹配会让环同时减少两条边,那么此时这个图又变成 了一棵树,我们可以继续按照上述方式匹配,直到没有度数为 1 的点。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::vector<int> > adj(n);
std::vector<int> deg(n);
for (int i = 0; i < m; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
++ deg[u]; ++ deg[v];
}
if (n == 1) {
std::cout << 1 << "\n";
return;
}
std::queue<int> q;
for (int i = 0; i < n; ++ i) {
if (deg[i] == 1) {
q.push(i);
}
}
int ans = 0;
std::vector<int> vis(n);
while (q.size()) {
int u = q.front(); q.pop();
if (vis[u]) {
continue;
}
vis[u] = 1;
bool flag = false;
for (auto & v : adj[u]) {
if (!vis[v]) {
flag = true;
vis[v] = 1;
for (auto & x : adj[v]) {
if (!vis[x] && -- deg[x] == 1) {
q.push(x);
}
}
break;
}
}
if (!flag) {
++ ans;
}
}
if (ans) {
std::cout << ans << "\n";
} else {
std::cout << "Yes\n";
}
}
E 斐波那契的压迫
因为卡d所以看了一眼,很明显是线段树,但感觉比较麻烦并且当时没时间了就继续去做d了。
待补
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具