牛客小白月赛30 题解
A.黑白边
类似于最小生成树Kruscal算法的思想,用并查集维护当前的连通块直到放入 条边则实现了两两联通,因为要白边最少,排个序就可以了。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 5; const int mod = 1e9 + 7; struct Edge { int u, v, w; bool operator < (const Edge &s) const { return w < s.w; } }edge[N << 1]; int F[N]; int findz(int x) { return F[x] = F[x] == x ? x : findz(F[x]); } int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int n, m; cin >> n >> m; for(int i = 1; i <= m; i++) { cin >> edge[i].u >> edge[i].v >> edge[i].w; } sort(edge + 1, edge + 1 + m); bool flag = 0; for(int i = 1; i <= n; i++) { F[i] = i; } int ans = 0, cnt = 0; for(int i = 1; i <= m; i++) { int u = edge[i].u, v = edge[i].v; int fx = findz(u), fy = findz(v); if(fx != fy) { F[fx] = fy; cnt++; if(edge[i].w) ans++; } if(cnt == n - 1) break; } if(cnt != n - 1) { cout << -1 << endl; } else { cout << ans << endl; } return 0; }
B. 最好的宝石
太久没写线段树忘了
update: 线段树模板题,多加一个mc表示最大值的个数,然后合并的过程中判断左右子区间的最大值是否相同搞一搞就行了。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5e5 + 5; typedef long long ll; struct node { int l, r, maxz, mc; }t[N << 2]; void push_up(int rt) { if(t[rt].l == t[rt].r) return ; t[rt].maxz = max(t[rt << 1].maxz, t[rt << 1 | 1].maxz); t[rt].mc = 0; if(t[rt].maxz == t[rt << 1].maxz) { t[rt].mc += t[rt << 1].mc; } if(t[rt].maxz == t[rt << 1 | 1].maxz) { t[rt].mc += t[rt << 1 | 1].mc; } } void build(int rt, int l, int r) { t[rt].l = l, t[rt].r = r; if(l == r) { cin >> t[rt].maxz; t[rt].mc = 1; return ; } int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); push_up(rt); } void update(int rt, int x, int y) { if(t[rt].l == t[rt].r) { t[rt].maxz = y; return ; } int mid = t[rt].l + t[rt].r >> 1; if(x <= mid) { update(rt << 1, x, y); } else { update(rt << 1 | 1, x, y); } push_up(rt); } pair<int, int> query(int rt, int ql, int qr) { if(ql <= t[rt].l && qr >= t[rt].r) { return make_pair(t[rt].maxz, t[rt].mc); } int mid = t[rt].l + t[rt].r >> 1; if(qr <= mid) { return query(rt << 1, ql, qr); } else if(ql > mid) { return query(rt << 1 | 1, ql, qr); } else { auto p1 = query(rt << 1, ql, mid); auto p2 = query(rt << 1 | 1, mid + 1, qr); if(p1.first == p2.first) { return make_pair(p1.first, p1.second + p2.second); } else if(p1.first > p2.first) { return make_pair(p1.first, p1.second); } else { return make_pair(p2.first, p2.second); } } } int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int n, m; cin >> n >> m; build(1, 1, n); while(m--) { string s; cin >> s; if(s == "Ask") { int l, r; cin >> l >> r; auto tmp = query(1, l, r); cout << tmp.first << ' ' << tmp.second << '\n'; } else { int x, y; cin >> x >> y; update(1, x, y); } } return 0; }
C. 滑板上楼梯
先写一个 找到方案数,然后观察答案,发现分布是 1,2,1,2,3,4,3,4, 其中规律的步长为4, 如果是奇数会比偶数少1, 这样我们可以归纳出答案 , 奇数对应减1。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 5; const int mod = 1e9 + 7; ll dp[N][2]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); memset(dp, 0x3f, sizeof(dp)); // dp[3][1] = 1; // dp[3][0] = 3; // dp[0][0] = 0; // for(int i = 1; i <= 20; i++) { // dp[i][0] = min(dp[i - 1][0], dp[i - 1][1]) + 1; // if(i - 3 >= 0) // dp[i][1] = dp[i - 3][0] + 1; // } // for(int i = 0; i <= 20; i++) { // cout << i << ' ' << min(dp[i][0], dp[i][1]) << endl; // } ll n; cin >> n; ll now = ((n - 1) / 4 + 1) * 2; if(n & 1) cout << now - 1 << endl; else cout << now << endl; return 0; }
D.GCD
显然 里所有的素数都要包含在 里面,于是答案就是 ,因为除了素数其他数字都会跟素数组合满足
特判 为 -1,因为全是素数
注意本题把1也认为是素数。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 5; const int mod = 1e9 + 7; int prime[N]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int n; cin >> n; if(n <= 3) { cout << -1 << endl; return 0; } for(int i = 2; i <= n; i++) { if(!prime[i]) { for(int j = 2 * i; j <= n; j += i) { prime[j] = 1; } } } int ans = 0; for(int i = 1; i <= n; i++) { ans += (!prime[i]); } cout << ans + 1 << endl; return 0; }
E.牛牛的加法
没什么好说的,一开始用 读入了,卡了很久改成 就行了
读入后记得把长度短的前面补0,然后对应位相加取模即可
注意00000、0001的情况,要分别输出0和1
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 5; const int mod = 1e9 + 7; ll ans[N]; int main() { //cout << (80 ^ 34) << endl; ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); string a, b; cin >> a >> b; while(a.size() < b.size()) a = '0' + a; while(a.size() > b.size()) b = '0' + b; vector<int> v1, v2; for(auto &x : a) { v1.push_back(x - '0'); } for(auto &x : b) { v2.push_back(x - '0'); } for(int i = 0; i < v1.size(); i++) { ans[i] = (v1[i] + v2[i]) % 10; //cout << v1[i] << ' ' << v2[i] << endl; } int cnt = 0; while(ans[cnt] == 0 && cnt < v1.size()) ++cnt; //cout << cnt << endl; if(cnt == v1.size()) { cout << 0 << endl; return 0; } //reverse(ans, ans + max(v1.size(), v2.size())); for(int i = cnt; i < v1.size(); i++) { cout << ans[i]; } cout << endl; return 0; }
F.石子合并
每次保留一个最大数字用来合并其他数字,这个最大数字可以有 次贡献( 次合并), 其他的数字分别都有一次贡献,答案为
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 5; const int mod = 1e9 + 7; ll a[N]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); //memset(dp, 0x3f, sizeof(dp)); int n; cin >> n; for(int i = 1; i <= n; i++) cin >> a[i]; ll sum = 0, maxz = *max_element(a + 1, a + 1 + n); ll cnt = 0; for(int i = 1; i <= n; i++) { sum += a[i]; } sum -= maxz; cout << sum + (n - 1) * maxz << endl; return 0; }
G.滑板比赛
贪心一下,对 个数字排序,从小到大每次在 个数字里 找即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 5; const int mod = 1e9 + 7; int a[N]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int n, m; cin >> n >> m; multiset<int> st; for(int i = 1; i <= n; i++) { cin >> a[i]; st.insert(a[i]); } for(int i = 1; i <= m; i++) { cin >> a[i]; } sort(a + 1, a + 1 + m); int ans = 0; for(int i = 1; i <= m; i++) { auto pos = st.upper_bound(a[i]); if(pos == st.end()) continue; ans++; st.erase(pos); } cout << ans << endl; return 0; }
H.第k小
以为是主席树,但是其实用两个优先队列就能做了,一个大根堆一个小根堆,大根堆里存 个数字,那么小根堆里的堆顶元素就是第 小。注意到插入操作可能会小于小根堆的堆顶,此时的操作是放到大根堆,再从大根堆取出堆顶放入小根堆。
注意元素不够输出-1
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 5; const int mod = 1e9 + 7; int a[N]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int n, m, k; cin >> n >> m >> k; priority_queue<int, vector<int>, greater<int> > q1; priority_queue<int> q2; for(int i = 1; i <= n; i++) cin >> a[i], q1.push(a[i]); while(q2.size() < k - 1 && !q1.empty()) { q2.push(q1.top()), q1.pop(); } for(int i = 1; i <= m; i++) { int op; cin >> op; if(op == 1) { int x; cin >> x; if(q2.size() < k - 1) { q1.push(x); while(q2.size() < k - 1 && !q1.empty()) { q2.push(q1.top()), q1.pop(); } } else if(q1.empty() || q1.top() > x) { q2.push(x); auto tmp = q2.top(); q2.pop(); q1.push(tmp); } } else { while(q2.size() < k - 1 && !q1.empty()) { q2.push(q1.top()), q1.pop(); } if(q1.size() && q2.size() == k - 1) cout << q1.top() << endl; else cout << -1 << endl; } } return 0; }
I.区间异或
观察数据范围,先 预处理一下每一个长度异或的最大值
最后每次查询的时候二分查找即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 5; const int mod = 1e9 + 7; int a[N], ans[N]; int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; for(int i = 1; i <= n; i++) { cin >> a[i]; a[i] ^= a[i - 1]; } for(int i = 1; i <= n; i++) { for(int j = i; j <= n; j++) { ans[j - i + 1] = max(ans[j - i + 1], a[j] ^ a[i - 1]); } } for(int i = 1; i <= n; i++) { ans[i] = max(ans[i], ans[i - 1]); } for(int i = 1; i <= m; i++) { int x; cin >> x; int p = lower_bound(ans + 1, ans + 1 + n, x) - ans; if(p == n + 1) { cout << "-1\n"; } else { cout << p << "\n"; } } return 0; }
J.小游戏
简单 , 取和不取用0/1表示,显然有转移方程
- dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]),
- dp[i][1] = dp[i - 1][0] + 1LL * i * vis[i],其中vis[i]是该数字出现的次数
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 5; const int mod = 1e9 + 7; int a[N], vis[N]; ll dp[N][2]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int n; cin >> n; for(int i = 1; i <= n; i++) { cin >> a[i]; vis[a[i]]++; } sort(a + 1, a + 1 + n); for(int i = 1; i <= a[n]; i++) { dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]); dp[i][1] = dp[i - 1][0] + 1LL * i * vis[i]; } cout << max(dp[a[n]][0], dp[a[n]][1]) << endl; return 0; }