2023牛客寒假算法基础集训营第五场题解(A-M 转本人知乎)
比赛链接:[2023牛客寒假算法基础集训营5]
比赛情况: 8/138/138/13
本场个人觉得难度排行: $$A≤C≤L≤H≤K≤D≤M≤G≤F≤E≤B≤I≤JA\le C\le L\le H\le K\le D\le M\le G\le F\le E\le B\le I\le JA\le C\le L\le H\le K\le D\le M\le G\le F\le E\le B\le I\le J$$
只A了8题,最后两个小时有事情要去拜年,去亲戚家了~~只好先润了!!!5555555
wtcl!!!!wwwwwwwwwww
赛时情况:
自认为 签到 A B K H 中等题:L C D I 难题 E F G J
A题 小沙の好客
二分+前缀和
void solve() {
int n, q;
cin >> n >> q;
vector<int>a(n + 10), sum(n + 10);
rep(i, 1, n)cin >> a[i];
sort(a.begin() + 1, a.begin() + 1 + n);
rep(i, 1, n)sum[i] = sum[i - 1] + a[i];
while (q--){
int k, x; cin >> k >> x;
int t = upper_bound(a.begin() + 1, a.begin() + 1 + n, x) - a.begin();
if (t == n + 1 || a[t] > x)t--;
cout << sum[t] - sum[max(t - k, 0LL)] << endl;
}
}
B题 小沙の博弈
void solve() {
int n; cin >> n;
if (n & 01)
cout << "Yaya-win!" << endl;
else cout << "win-win!" << endl;
}
K题 小沙の抱团 easy
void solve() {
int n; cin >> n;
int res = 0;
while (n > 2) {
res++;
if (n & 01) {
n++;
n = n / 2;
}
else {
n++; n++;
n >>= 1;
}
}
cout << res << endl;
}
H题 小沙の店铺
void solve() {
int x, y, k, n, t;
cin >> x >> y >> k >> n >> t;
int ans = 0, cn = 0;
per(i, n, 1) {
ans += x * i;
cn += i;
if (cn >= k)
x += ((int)cn / k) * y, cn %= k;
if (ans >= t) {
out(n - i + 1);
return;
}
}
out(-1);
}
L题 小沙の抱团 hard
DP
dp[N]含义为 当最小为i时的最小代价
背包思想
void solve() {
int n, M; cin >> n >> M;
vector<int>w(M + 10), x(M + 10), dp(100000 + 10, inf);
rep(i, 1, M)cin >> w[i] >> x[i];
if (n == 1 || n == 2) {
out(0);
return;
}
dp[n] = 0;
per(j, n, 1) {
rep(i, 1, M) {
int W = w[i], X = x[i];
if (X > j)continue;
dp[j - j % X] = min(dp[j - j % X], dp[j] + W);
}
}
int ans = 0;
rep(i, 1, n) {
if (dp[i] != dp[100001]) {
out(dp[i]);
return;
}
}
}
C题 小沙の不懂
用用sjjj的题解
由于当字符串长度不等时,一定存在长的大于短的,那么我们仅需要去寻找当长字符串尽可能小时, 是否小于短字符串即可判断,这里可以根据自己的思路自由发挥。
int main() {
string s, t;
cin >> s >> t;
if (s.size() != t.size()) {
bool flag = 0;
if (s.size() < t.size())flag = 1, swap(s, t);
set<char>ft;
for (ri i = 0; i < s.size() - t.size(); ++i)ft.insert(s[i]);
if (ft.size() > 1)return putchar(flag ? '<' : '>'), 0;
string _s = s.substr(s.size() - t.size(), t.size());
for (ri i = 0; i < t.size(); ++i)
if (_s[i] != t[i]) {
if (t[i] == s[0])return putchar(flag ? '<' : '>'), 0;
break;
}
putchar('!');
}
else putchar(s == t ? '=' : '!');
return 0;
}
D题 小沙の赌气
赛中我直接用set模拟了,但是赛后我想用动态开点的权值线段树写写,之后补吧
void solve()
{
int n;
cin >> n;
vector<node> a(n + 10), b(n + 10);
rep(i, 1, n)cin >> a[i].l >> a[i].r, ++a[i].r;
rep(i, 1, n)cin >> b[i].l >> b[i].r, ++b[i].r;
set <node> pa, pb;
int va = 1, vb = 1;
auto cacl = [](set<node>& p, node x, int& h) {
p.insert(x);
while (!p.empty() && h >= p.begin()->l) {
h = max(h, p.begin()->r);
p.erase(p.begin());
}
};
rep(i, 1, n) {
cacl(pb, b[i], vb);
cacl(pa, a[i], va);
if (va < vb)
out("ya_win!");
else if (va == vb)
out("win_win!");
else
out("sa_win!");
out(abs(va - vb));
}
}
I题 小沙の金银阁
这思维题 要想不亏 那n-1轮肯定是前缀和<=第n轮,尽可能等于
void solve() {
int n, m;
cin >> n >> m;
vector<int>a(n + 10);
per(i, n, 1)
{
if (i != 1)
a[i] = (m + 1) >> 1;
else
a[i] = m;
m -= a[i];
}
if (!a[1]) {
out(-1); return;
}
rep(i, 1, n)
cout << a[i] << " \n"[i == n];
}
F题 小沙の串串
写这题前我们先AC下这题 力扣 402. 移掉 K 位数字这题
class Solution {
public:
#define rep(i,a,n) for (int i=a;i<=n;i++)
string removeKdigits(string s = "1432219", int k = 3) {
string str;
vector<int>st;
int len = s.size();
rep(i, 0, len - 1) {
while (!st.empty() && k > 0 && st.back() > s[i] - '0'){
st.pop_back();
k--;
}
st.push_back(s[i] - '0');
}
while (k--)st.pop_back();
for (auto& i : st)str += i + '0';
int cn = 0;
while (str[cn] == '0')cn++;
str = str.substr(cn, (int)str.size());
return str.empty() ? "0" : str;
}
};
这题不同点在于,我们可以对几个进行sort
void solve() {
int n, k;
string s, ans, res;
cin >> n >> k >> s;
rep(i, 0, n - 1) {
while (k && !ans.empty() && ans.back() < s[i])
k--, res.push_back(ans.back()), ans.pop_back();
ans.push_back(s[i]);
}
while(k&&!ans.empty())
k--, res.push_back(ans.back()), ans.pop_back();
sort(res.rbegin(), res.rend());
cout << ans + res << endl;
}
G题 小沙の编码
网络流
我们已经知道a[1],a[3],a[5]......的数值,需要找出剩下n/2个元素
我们需要合法的边,那么我们只需要进行匹配,a[2]=now,now既要满足与a[1]二进制差一,也要满足与a[3]二进制差一,我们直接枚举二进制位异或就可以了,预处理p2数组
DINIC<N> G;
void solve() {
vector<int>p2(N);
rep(i, 0, 17)p2[1LL << i] = i;
p2[1] = 1;
int n; cin >> n;
vector<int>a(n + 10, 0);
vector<bool>vis(N, false);
for (int i = 1; i <= n; i += 2)cin >> a[i], vis[a[i]] = true;
//1 2 st
G.S = 1, G.T = 2;
memset(G.head, -1, sizeof G.head);
int m = p2[n];
for (int i = 2; i <= n - 2; i += 2)
{
G.add(1, 2 + i, 1), G.add(2 + i, 1, 0);
rep(j, 0, m - 1) {
int now = a[i - 1] ^ (1LL << j);
if (p2[now ^ a[i + 1]] && !vis[now])
{
G.add(2 + i, 3 + n + now, 1);
G.add(3 + n + now, 2 + i, 0);
}
}
}
G.add(1, 2 + n, 1); G.add(2 + n, 1, 0);
//特判最后一个点
rep(j, 0, m - 1) {
int now = a[n - 1] ^ (1LL << j);
if (!vis[now])
{
G.add(2 + n, 3 + n + now, 1);
G.add(3 + n + now, 2 + n, 0);
}
}
//二分图左边连右边建立好边
rep(i, 0, n - 1)
G.add(3 + n + i, 2, 1), G.add(2, 3 + n + i, 0);
G.Dinic();
for (int i = 2; i <= n; i += 2) {
for (int j = G.head[i + 2]; ~j; j = G.edge[j].next)
{
int flow = G.edge[j].cap;
if (!flow && G.edge[j].to >= n + 3)
a[i] = G.edge[j].to - n - 3;
}
}
rep(i, 1, n)cout << a[i] << " \n"[i == n];
}
E题 小沙の印章
不懂 看题解还是晕嘘嘘
构造
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
using LL = long long;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
void solve(){
int n;
cin >> n;
vector<int> a(n);
iota(a.begin(), a.end(), 1);
vector<vector<PII>> ans;
while(!is_sorted(a.begin(), a.end(), greater<int>())){
vector<PII> seg;
for(int i = 1; i < n; i ++ ){
if(a[i] > a[i - 1]){
seg.push_back({a[i], a[i - 1]});
swap(a[i], a[i - 1]);
i ++ ;
}
}
ans.push_back(seg);
}
cout << ans.size() << endl;
for(auto v: ans){
cout << v.size() << endl;
for(auto [x, y] : v){
cout << x << " " << y << endl;
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}
J题 小沙の最短路
构造 有缘再补吧
本人这篇文章发布于 知乎
发布于 2023-02-06 22:53・IP 属地安徽