文章目录
2020牛客暑期多校训练营(第三场)题解及补题
比赛过程
首先L是签到,然后过了AB两签到,然后按顺序写了C、F、E三题。总的来说,前期写AB的时候,队内的交流不够充分,因为本来以为都是签到自顾自写没什么问题,结果白白wa了很多发,以后的比赛中除了L这种签到程度的签到,我们还是应该通过交流来保证思路和算法的正确性。然后中后期过的三个题,队的交流还是比较充分的,相比前几场有了明显提升。可惜的是D有了完整思路只有一个细节有小错误来不及改了,以后或许可以在比赛时有更优的分配策略。赛后补了D和G。总的来说这场偏思维,我们的排名也比之前要高一点,但是也可以发现我们的弱侧还是在于算法的技能树,需要多学习专项的算法。
题解
A
题意
n个台阶,每个台阶可能有鱼或者蛤蜊共四种组合可能,然后有四种选择,分别是有鱼的情况下直接拿鱼,或者捡起蛤蜊变成鱼饵,或者把鱼饵换成鱼,或者什么都不做。
解法
有鱼拿鱼,没鱼的话唯一决策点在于只有蛤蜊的时候是拿蛤蜊还是鱼饵换鱼。我们可以弄个后缀记录这点后面可以把鱼饵变成鱼的点(即0或1个数和),若当前鱼饵数比可变点少则拿蛤蜊,反之换鱼。
代码
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const ll maxn = 2e6 + 111;
char s[maxn];
int tail[maxn];
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
scanf("%s", s + 1);
int ans = 0, fb = 0;
for (int i = 1; i <= n; ++i) {
if (s[i] == '3' || s[i] == '2') {
++ans;
}
}
tail[n + 1] = 0;
for (int i = n; i >= 1; --i) {
if (s[i] == '0' || s[i] == '1') {
tail[i] = tail[i + 1] + 1;
} else
tail[i] = tail[i + 1];
}
for (int i = 1; i <= n; ++i) {
if (s[i] == '1') {
if (fb < tail[i + 1]) {
++fb;
} else {
if (fb)
--fb, ++ans;
else
++fb;
}
} else if (s[i] == '0') {
if (fb) ++ans, --fb;
}
}
printf("%d\n", ans);
}
return 0;
}
B
题意
给了一个字符串和 q q q 次操作,每次操作可以将左边长度为 x x x 的子串移动到最右边或者将右边长度为 x x x 的子串移动到最左边,或者输出 s x s_x sx 。
解法
把字符串想象成头尾相接的,那么移动的过程就相当于第一个字符发生了变化,所以可以 O ( 1 ) O(1) O(1) 操作每次询问。
代码
#pragma region
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#define debug() printf(" ");
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(int64_t &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <class T, class... U>
void R(T &head, U &... tail) { _R(head), R(tail...); }
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
for (auto i = x.begin(); i != x.end(); _W(*i++))
if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
int main() {
IO;
string s;
cin >> s;
int q;
cin >> q;
int pos = 0, len = s.length();
while (q--) {
char a;
int x;
cin >> a >> x;
if (a == 'A')
cout << s[(pos + len + x - 1) % len] << endl;
else {
pos += x;
pos = (pos + len) % len;
}
}
}
C
题意
给出一个固定大小和形状的左手(右手与之对称)。然后给你一只手的坐标点(可旋转、不可伸缩)。判断是左手还是右手。
解法
容易找到底边A、B,从而找到大拇指的点C(与B相连),BC与BA叉积即可判断他们位置关系。
代码
#pragma region
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#define debug() printf(" ");
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(int64_t &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <class T, class... U>
void R(T &head, U &... tail) { _R(head), R(tail...); }
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
for (auto i = x.begin(); i != x.end(); _W(*i++))
if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
typedef pair<double, double> pdd;
const double eps = 1e-4;
inline int sgn(const double &x) { return x < -eps ? -1 : x > eps; }
inline bool equa(double A, double B) { return sgn(A - B) == 0; }
inline double dis(pdd A, pdd B) { return sqrt((A.first - B.first) * (A.first - B.first) + (A.second - B.second) * (A.second - B.second)); }
inline double det(pdd A, pdd B) { return A.first * B.second - A.second * B.first; }
int main() {
int T;
R(T);
while (T--) {
vector<pdd> v;
int n = 20;
rep(i, 1, 20) {
double x, y;
R(x, y);
v.push_back({x, y});
}
int p1 = -1, p2 = -1, p3 = -1, p4 = -1; //p2,p3是(1,0)
rep(i, 0, 19) {
if (equa(dis(v[i], v[(i + 1) % n]), 6.0)) p1 = i, p2 = (i + 1) % n;
if (equa(dis(v[i], v[(i + 1) % n]), 9.0)) p3 = i, p4 = (i + 1) % n;
}
// W(p1, p2, p3, p4);
// W(dis(v[p1], v[p2]));
// W(dis(v[p3], v[p4]));
if (p1 == p3) swap(p1, p2);
if (p1 == p4) swap(p1, p2), swap(p3, p4);
if (p2 == p4) swap(p3, p4);
if (det({v[p1].first - v[p2].first, v[p1].second - v[p2].second}, {v[p4].first - v[p3].first, v[p4].second - v[p3].second}) > 0)
W("left");
else
W("right");
}
}
D
题意
无限大格点平面,问能否通过涂黑n个点来得到m个相邻异色点对。
解法
假设连续的一片点的个数为n,预处理找到每个n对应的最少点数和最多点数,可以发现以2为单位变换,所以m必须是偶数。再者对于给出的n,假设连续的点的个数为i,去遍历i寻找答案即可。比赛时对于最少点数的求法出了错,应该是对于ab>=n的a、b 去寻找2(a+b),而不是a*b==n的a、b。
代码
#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const ll maxn = 2e5 + 111;
ll num[100];
ll mp[1000][1000] = {0};
pair<ll, ll> pp[100];
int main() {
num[1] = 4;
pp[1] = {1, 1};
for (ll i = 2; i <= 50; ++i) {
ll j;
for (j = 1; j <= i; ++j) {
if (j * j >= i) {
break;
}
}
num[i] = (j + (i + j - 1) / j) * 2;
pp[i] = {j, (i + j - 1) / j};
}
ll T;
scanf("%lld", &T);
while (T--) {
ll n, m;
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= 100; ++i) {
for (int j = 1; j <= 100; ++j) {
mp[i][j] = 0;
}
}
if ((m & 1) || (m > 4 * n)) {
puts("No");
} else {
ll flag = 0;
ll depe = 0;
ll trans = 0;
ll nnn, mmm;
for (ll i = 1; i <= n; ++i) {
// i个连续点
ll num_d = i;
ll minn = num[i] + 4 * (n - i), maxx = 2 * i + 2 + 4 * (n - i);
if (m <= maxx && m >= minn) {
flag = 1;
depe = n - i;
trans = (m - minn) / 2;
nnn = pp[i].first, mmm = pp[i].second;
for (int ii = pp[i].first; ii >= 1; --ii) {
for (int jj = pp[i].second; jj >= 1; --jj) {
mp[ii][jj] = 1;
--num_d;
if (!num_d) break;
}
if (!num_d) break;
}
break;
}
}
if (!flag) {
puts("No");
} else {
puts("Yes");
for (int i = 1, num = 1; num <= depe; ++num, i += 5) {
printf("%d %d\n", i, 10000000);
}
int ni = 1, nj = 1;
while (mp[ni][nj] == 0) {
++nj;
if (nj == mmm) {
nj = 1;
++ni;
}
}
int toi = nnn, toj = mmm + 1;
while (trans--) {
mp[ni][nj] = 0;
++nj;
if (nj == mmm) {
nj = 1;
++ni;
}
mp[toi][toj++] = 1;
}
for (int i = 1; i <= 100; ++i) {
for (int j = 1; j <= 100; ++j) {
if (mp[i][j]) {
printf("%d %d\n", i, j);
}
}
}
}
}
}
return 0;
}
E
题意
题面很绕,本质上就是对于数组a,去找两个完全不同的匹配,使得匹配之间差值和最小。
解法
可以发现,4个点或者6个点之中,最优的策略下的答案就是最后一个数减第一个数。由此我们以4or6为一个单位进行dp即可。在每个偶数点位更新答案。
代码
#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const ll maxn = 2e5 + 111;
ll a[maxn];
ll dp[maxn];
int main() {
ll T;
scanf("%lld", &T);
while (T--) {
ll n;
scanf("%lld", &n);
for (ll i = 1; i <= n; ++i) {
scanf("%lld", a + i);
}
sort(a + 1, a + 1 + n);
ll ans = 0;
dp[4] = (a[4] - a[1]) * 2;
dp[6] = (a[6] - a[1]) * 2;
dp[8] = dp[4] + (a[8] - a[5]) * 2;
for (ll i = 10; i <= n; i += 2) {
dp[i] = min(dp[i - 4] + (a[i] - a[i - 3]) * 2,
dp[i - 6] + (a[i] - a[i - 5]) * 2);
}
printf("%lld\n", dp[n]);
}
return 0;
}
F
题意
给出a与b找到c、d、e、f使得 c d − e f = a b \frac{c}{d}-\frac{e}{f}=\frac{a}{b} dc−fe=ba且$ d< b $ and $ f < b、1<=c,e<=4e12 $。
解法
首先b为1则直接-1,然后a、b不互质易得答案,然后若b为质数或者只有一个质因子,显然也是-1,最后b不为质数则可以拆成俩互质的数n、m,从而解cm-en=a即可,直接exgcd。
代码
#pragma region
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#define debug() printf(" ");
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(int64_t &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <class T, class... U>
void R(T &head, U &... tail) { _R(head), R(tail...); }
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
for (auto i = x.begin(); i != x.end(); _W(*i++))
if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
const int maxn = 2e6 + 5;
ll mp[maxn];
// vector<int> prime;
void find() {
for (ll i = 2; i < maxn; ++i) {
if (mp[i]) continue;
// prime.push_back(i);
for (ll j = i * i; j < maxn; j += i)
mp[j] = i;
}
}
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (!b) {
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll t = x;
x = y;
y = t - (a / b) * y;
return d;
}
pair<ll, ll> cal(ll n, ll m, ll a) {
ll x, y;
ll dd = exgcd(n, m, x, y);
ll d1 = n / dd;
ll d2 = m / dd;
if (x < 0) {
ll cnt = x / d2 + 1;
x += cnt * d2, y -= cnt * d1;
}
if (y > 0) {
ll cnt = y / d1 + 1;
x += cnt * d2, y -= cnt * d1;
}
return {a * x, -a * y};
}
int main() {
int T;
R(T);
find();
while (T--) {
ll a, b;
R(a, b);
ll g = __gcd(a, b);
if (b == 1) {
W("-1 -1 -1 -1");
} else if (g != 1) {
a /= g, b /= g;
W(a + 1, b, 1, b);
} else if (!mp[b]) { //b是质数
W("-1 -1 -1 -1");
} else {
ll b1 = mp[b], b2 = 1;
while (b % b1 == 0) b /= b1, b2 *= b1;
if (b == 1) {
W("-1 -1 -1 -1");
} else {
pair<ll, ll> ans = cal(b2, b, a);
W(ans.first, b, ans.second, b2);
}
}
}
}
G
题意
给你 n n n 个点, m m m 条边,一开始,所有点自身是一个group,一共 q q q 次操作,每次操作将所有与这个group中的点相连的点合并成一个group,求所有操作后每个点所在的group。
解法
并查集维护点的group信息,每次将与u相连的点v合并的时候,就把这个点相连的所有信息合并到u中即可。
代码
#pragma region
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#define debug() printf(" ");
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(int64_t &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <class T, class... U>
void R(T &head, U &... tail) { _R(head), R(tail...); }
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f ", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
for (auto i = x.begin(); i != x.end(); _W(*i++))
if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
const int maxn = 8e5 + 5;
vector<int> g[maxn];
int pre[maxn], n, m;
int findroot(int x) { return pre[x] == x ? x : pre[x] = findroot(pre[x]); }
void link(int x, int y) { pre[findroot(x)] = pre[findroot(y)]; }
void init() {
rep(i, 0, n - 1) {
pre[i] = i;
g[i].clear();
}
}
int main() {
int T;
R(T);
while (T--) {
R(n, m);
init();
while (m--) {
int u, v;
R(u, v);
g[u].push_back(v);
g[v].push_back(u);
}
int q;
R(q);
while (q--) {
int x;
R(x);
if (x != pre[x]) continue;
vector<int> t;
for (auto u : g[x]) {
int uu = findroot(u);
if (x != uu) {
if (t.size() < g[uu].size()) swap(g[uu], t);
for (auto v : g[uu]) t.push_back(v);
g[uu].clear();
link(uu, x);
}
}
swap(t, g[x]);
}
rep(i, 0, n - 1) printf("%d%c", findroot(i), " \n"[i == n - 1]);
}
}
H
题意
解法
代码
//将内容替换成代码
I
题意
解法
代码
//将内容替换成代码
J
题意
判断串是否是lovely开头,对大小写不区分。
解法
签到,直接判断。
代码
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include <bits/stdc++.h>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
//#include <ext/pb_ds/priority_queue.hpp>
//using namespace __gnu_pbds;
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define mst(a, b) memset((a), (b), sizeof(a))
#define mp(a, b) make_pair(a, b)
#define pi acos(-1)
#define endl '\n'
#define pii pair<int, int>
#define pll pair<ll, ll>
#define pdd pair<double, double>
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define lowbit(x) x &(-x)
#define all(x) (x).begin(), (x).end()
#define sf(x) scanf("%d", &x)
#define pf(x) printf("%d", x)
#define debug(x) cout << x << endl
#define mod(x) (x % mod + mod) % mod
template <typename T>
void read(T &x)
{
x = 0;
char ch = getchar();
ll f = 1;
while (!isdigit(ch))
{
if (ch == '-')
f *= -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - 48;
ch = getchar();
}
x *= f;
}
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const int maxn = 1e5 + 7;
const int maxm = 1e5 + 7;
const int mod = 1e9 + 7;
#define IO ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
//template<typename T> using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
int main()
{
IO;
string s;
cin>>s;
if ((s[0]=='l'||s[0]=='L')&&(s[1]=='o'||s[1]=='O')&&(s[2]=='v'||s[2]=='V')&&(s[3]=='e'||s[3]=='E')&&(s[4]=='l'||s[4]=='L')&&(s[5]=='y'||s[5]=='Y'))
cout<<"lovely\n";
else
cout<<"ugly\n";
return 0;
}