牛客小白月赛30
A. 黑白边
题解
并查集模板题,构成最小生成树,优先选择黑边。
注意不能构成树的情况,吃了一发罚时。
代码
#include <cmath> #include <cstdio> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const ll mod = 1e9+7; const int maxn = 2e5+55; struct node { int x, y, z; }num[maxn]; int bin[maxn]; void init(int n) { for (int i = 1; i <= n; ++ i) { bin[i] = i; } } int find(int x) { return bin[x] = (x == bin[x] ? x : find(bin[x])); } int main() { int n, m; cin >> n >> m; init(n); for (int i = 0; i < m; ++ i) { cin >> num[i].x >> num[i].y >> num[i].z; } sort(num, num+m, [](node x, node y) { return x.z < y.z; }); int ans = 0, cnt = n-1; for (int i = 0; i < m; ++ i) { int xx = find(num[i].x); int yy = find(num[i].y); if (xx != yy) { if (num[i].z) ans ++; cnt --; bin[xx] = yy; } } if (cnt) cout << -1 << endl; else cout << ans << endl; return 0; }
B. 最好的宝石
题解
线段树,每个节点中存放这个节点所代表的最左区间 l
,最右区间 r
, 区间 [l,r]
之间的最大值以及最大值出现的次数。
struct node { int l, r, cnt, maxx; };
注意区间合并就好了吧
tree[step].maxx = max(tree[step<<1].maxx, tree[step<<1|1].maxx); if (tree[step].maxx == tree[step<<1].maxx) tree[step].cnt += tree[step<<1].cnt; if (tree[step].maxx == tree[step<<1|1].maxx) tree[step].cnt += tree[step<<1|1].cnt;
更新区间的时候需要注意的是
tree[step].cnt = 0;
代码
#include <queue> #include <cmath> #include <cstdio> #include <string> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const int maxn = 2e5+55; struct node { int l, r, cnt, maxx; }tree[maxn<<2]; int a[maxn]; void Build(int l, int r, int step) { tree[step].l = l; tree[step].r = r; tree[step].cnt = 0; tree[step].maxx = 0; if (l == r) { tree[step].cnt = 1; tree[step].maxx = a[l]; return; } int mid = (l+r) >> 1; Build(l, mid, step<<1); Build(mid+1, r, step<<1|1); tree[step].maxx = max(tree[step<<1].maxx, tree[step<<1|1].maxx); if (tree[step].maxx == tree[step<<1].maxx) tree[step].cnt += tree[step<<1].cnt; if (tree[step].maxx == tree[step<<1|1].maxx) tree[step].cnt += tree[step<<1|1].cnt; } void Update(int pos, int val, int step) { if (tree[step].l > pos || tree[step].r < pos) return; if (tree[step].l == tree[step].r) { tree[step].cnt = 1; tree[step].maxx = val; return; } Update(pos, val, step<<1); Update(pos, val, step<<1|1); tree[step].maxx = max(tree[step<<1].maxx, tree[step<<1|1].maxx); tree[step].cnt = 0; if (tree[step].maxx == tree[step<<1].maxx) tree[step].cnt += tree[step<<1].cnt; if (tree[step].maxx == tree[step<<1|1].maxx) tree[step].cnt += tree[step<<1|1].cnt; } int maxx = 0, cnt = 0; void Query(int l, int r, int step) { if (tree[step].l > r || tree[step].r < l) return; if (tree[step].l >= l && tree[step].r <= r) { if (maxx == tree[step].maxx) { cnt += tree[step].cnt; } if (maxx < tree[step].maxx) { maxx = tree[step].maxx; cnt = tree[step].cnt; } return; } Query(l, r, step<<1); Query(l, r, step<<1|1); } string s; int main() { int n, m; cin >> n >> m; for (int i = 1; i <= n; ++ i) { cin >> a[i]; } Build(1, n, 1); while (m --) { int x, y; cin >> s >> x >> y; if (s[0] == 'A') { maxx = 0, cnt = 0; Query(x, y, 1); cout << maxx << " " << cnt << endl; } else { Update(x, y, 1); } } return 0; }
C. 滑板上楼梯
题解
根据贪心思路,优先走 3
台阶,那么最优的话就会形成
31313131....
等,这样走2
步上 4
台阶,后面的进行特判即可,
代码
#include <cmath> #include <cstdio> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const ll mod = 1e9+7; int main() { ll n; cin >> n; ll x = n/4*2; n %= 4; if (n == 3) x ++; else x += n; cout << x << endl; return 0; }
D. GCD
题解
考虑一下最复杂的情况,根据抽屉原则,把这个区间之内的所有的质数全部放到子集 T
中,再添加一个非 1
并且没有出现过的数字,必定存在两个数的 gcd
不为 1
。
所以素数筛这个区间的素数,最后的结果加 2
即可。
注意 -1
的情况。
代码
#include <cmath> #include <cstdio> #include <string> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const ll mod = 1e9+7; const int PSZ = 1e7+7; int isprimes[PSZ]; int primes[PSZ/10]; int Prime(int n) { int cnt = 0; isprimes[0] = isprimes[1] = 0; for (int i = 2; i <= n; ++ i) { isprimes[i] = 1; } for (int i = 2; i <= n; ++ i) { if (isprimes[i]) { primes[++cnt] = i; } for (int j = 1; j <= cnt && 1ll*primes[j]*i <= n; ++ j) { isprimes[i*primes[j]] = 0; if (i % primes[j] == 0) break; } } for (int i = 1; i <= n; ++ i) { isprimes[i] += isprimes[i-1]; } return cnt; } int main() { int n; cin >> n; int x = Prime(n); x += 2; if (x > n) { cout << -1 << endl; } else cout << x << endl; return 0; }
E. 牛牛的加法
题解
模拟吧,注意坑点,
- 删除前导
0
- 如果所有的结果都为
0
的话,记得输出一个0
代码
#include <cmath> #include <cstdio> #include <string> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const ll mod = 1e9+7; const int maxn = 2e5+555; string s, t; int a[maxn] = {0}; int main() { cin >> s >> t; int n = max(s.length(), t.length()); for (int i = 0; i < s.length(); ++ i) { a[i] += s[s.length()-1-i] - '0'; } for (int i = 0; i < t.length(); ++ i) { a[i] += t[t.length()-1-i] - '0'; } for (int i = 0; i < n; ++ i) { a[i] %= 10; } bool flag = false; for (int i = n-1; i >= 0; -- i) { if (a[i]) flag = true; if (flag) cout << a[i]; } if (!flag) cout << 0; cout << endl; return 0; }
F. 石子合并
题解
首先考虑一下,最后的结果为 2n-2
个石子的和并且每个石子至少没选择的了一次。
根据这个原则的话,优先选择,除了最大的石子以外的石子都被选择一次,其余的 n-1
个石子都为最大的才是最优的结果。
代码
#include <cmath> #include <cstdio> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const ll mod = 1e9+7; int main() { int n; cin >> n; ll maxx = 0, ans = 0, x; for (int i = 0; i < n; ++ i) { cin >> x; maxx = max(maxx, x); ans += x; } ans -= maxx; ans += maxx*(n-1); cout << ans << endl; return 0; }
G. 滑板比赛
题解
双指针算法,模拟+贪心
对于牛妹的每个值优先选择大于它的第一个值就好了。
代码
#include <queue> #include <cmath> #include <cstdio> #include <string> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const ll mod = 1e9+7; const int maxn = 2e5+55; int a[maxn], b[maxn]; int main() { int n, m; cin >> n >> m; for (int i = 0; i < n; ++ i) { cin >> a[i]; } for (int i = 0; i < m; ++ i) { cin >> b[i]; } sort(a, a+n); sort(b, b+m); int pos = 0; for (int i = 0; i < n; ++ i) { if (pos < m && a[i] > b[pos]) { pos ++; } } cout << pos << endl; return 0; }
H. 第 k 小
题解
优先队列+模拟?
代码
#include <queue> #include <cmath> #include <cstdio> #include <string> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const ll mod = 1e9+7; priority_queue<int> que; const int maxn = 2e5+55; int a[maxn]; int main() { int n, m, k; cin >> n >> m >> k; for (int i = 0; i < n; ++ i) { cin >> a[i]; } sort(a, a+n); for (int i = 0; i < n && i < k; ++ i) { que.push(a[i]); } int cnt = min(n, k); while (m --) { int x, y; cin >> x; if (x == 1) { cin >> y; cnt ++; que.push(y); if (cnt > k) { cnt --; que.pop(); } } else { if (cnt == k) { cout << que.top() << endl; } else cout << -1 << endl; } } return 0; }
I. 区间异或
题解
枚举区间 [l,r]
之间的所有异或值。
for (int i = 0; i < n; ++ i) { int ans = 0; for (int j = i; j < n; ++ j) { ans ^= a[j]; dp[i][j] = ans; } } for (int i = 0; i < n; ++ i) { for (int j = i; j < n; ++ j) { v.push_back({dp[i][j], j-i+1}); } }
排序+单调栈处理一下,目的是为了求出,区间 [l,r]
的最大值是多少。
sort(v.begin(), v.end(), [](node x, node y) { return x.val < y.val; }); for (int i = 0; i < (int)v.size(); ++ i) { while (u.size() && v[i].site <= u[u.size()-1].site) { u.pop_back(); } u.push_back(v[i]); } for (int i = 0; i < (int)u.size(); ++ i) { xt[i] = u[i].val; }
m
次查询,二分查找一下。
while (m --) { int x; cin >> x; if (x > u[u.size()-1].val) { cout << -1 << endl; } else { int pos = lower_bound(xt, xt+u.size(), x) - xt; cout << u[pos].site << endl; } }
代码
#include <queue> #include <cmath> #include <vector> #include <cstdio> #include <string> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const ll mod = 1e9+7; const int maxn = 3000+55; int dp[maxn][maxn]; int a[maxn]; struct node { int val, site; }; vector<node> v, u; int xt[maxn]; int main() { int n, m; cin >> n >> m; for (int i = 0; i < n; ++ i) { cin >> a[i]; } for (int i = 0; i < n; ++ i) { int ans = 0; for (int j = i; j < n; ++ j) { ans ^= a[j]; dp[i][j] = ans; } } for (int i = 0; i < n; ++ i) { for (int j = i; j < n; ++ j) { v.push_back({dp[i][j], j-i+1}); } } sort(v.begin(), v.end(), [](node x, node y) { return x.val < y.val; }); for (int i = 0; i < (int)v.size(); ++ i) { while (u.size() && v[i].site <= u[u.size()-1].site) { u.pop_back(); } u.push_back(v[i]); } for (int i = 0; i < (int)u.size(); ++ i) { xt[i] = u[i].val; } while (m --) { int x; cin >> x; if (x > u[u.size()-1].val) { cout << -1 << endl; } else { int pos = lower_bound(xt, xt+u.size(), x) - xt; cout << u[pos].site << endl; } } return 0; }
J. 小游戏
题解
dp
瞎搞。。。。。
代码
#include <queue> #include <cmath> #include <cstdio> #include <string> #include <iostream> #include <algorithm> using namespace std; #define endl "\n" typedef long long ll; typedef unsigned long long ull; const ll mod = 1e9+7; const int maxn = 2e5+555; ll dp[maxn] = {0}; int main() { int n, x; cin >> n; for (int i = 0; i < n; ++ i) { cin >> x; dp[x] += x; } ll maxx = 0; for (int i = 0; i <= 200000; ++ i) { if (i == 2) dp[i] += dp[i-2]; if (i >= 3) dp[i] += max(dp[i-2], dp[i-3]); } for (int i = 0; i <= 200050; ++ i) { // cout << dp[i] << " "; maxx = max(maxx, dp[i]); } // cout << endl; cout << maxx << endl; return 0; }