比赛链接
牛客周赛 Round 66
A题
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
int x, y, z;
void solve()
{
cin >> x >> y >> z;
cout << max(x + y + z - max({x, y, z}), max({x, y, z})) << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}
B题
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n;
void solve()
{
cin >> n;
for (int i = n; i >= 1; i--)
{
cout << i << " ";
}
cout << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}
C题
思路
用指针模拟循环移动字符串即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
string s;
void solve()
{
cin >> s;
int ans = 0;
for (int i = s.size() - 1, j = 1; i >= 0; i--, j *= 10)
{
ans += (s[i] - '0') * j;
}
int len = s.size();
for (int i = 1; i < s.size(); i++)
{
int sum = 0;
for (int cnt = 1, j = i; cnt <= s.size(); cnt++, j++)
{
j = j % len;
sum = sum * 10 + (s[j] - '0');
}
ans = min(ans, sum);
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}
D题
思路
数据范围很小,只用一个并查集来维护所有的联通块,然后区间修改时直接暴力枚举。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef pair<int, int> pii;
const int N = 2e3 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n, m;
int sum[N];
double a[N];
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> sum[i];
a[i] = sum[i];
sum[i] += sum[i - 1];
}
DSU dsu(n + 1);
while (m--)
{
int op, l, r;
cin >> op >> l;
if (op == 1)
{
cin >> r;
while (l > 1)
{
if (dsu.same(l - 1, l)) l--;
else break;
}
while (r < n)
{
if (dsu.same(r, r + 1)) r++;
else break;
}
double res = sum[r] - sum[l - 1];
double val = res / (r - l + 1);
for (int i = l; i <= r; i++)
{
a[i] = val;
}
for (int i = l + 1; i <= r; i++)
{
dsu.merge(i, i - 1);
}
}
else
{
cout << fixed << setprecision(10) << a[l] << endl;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}
E题
思路
我们用两个并查集分别维护其左边的祖先和右边的祖先,然后用线段树来维护区间赋值操作即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n, m;
int sum[N];
double a[N];
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
struct segmenttree
{
struct node
{
int l, r;
double maxx, tag;
};
vector<node>tree;
segmenttree(): tree(1) {}
segmenttree(int n): tree(n * 4 + 1) {}
void pushup(int u)
{
auto &root = tree[u], &left = tree[u << 1], &right = tree[u << 1 | 1];
root.maxx = max(left.maxx, right.maxx);
}
void pushdown(int u)
{
auto &root = tree[u], &left = tree[u << 1], &right = tree[u << 1 | 1];
if (root.tag != 0)
{
left.tag = root.tag;
right.tag = root.tag;
left.maxx = root.maxx;
right.maxx = root.maxx;
root.tag = 0;
}
}
void build(int u, int l, int r)
{
auto &root = tree[u];
root = {l, r};
if (l == r)
{
root.maxx = a[r];
}
else
{
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int l, int r, double val)
{
auto &root = tree[u];
if (root.l >= l && root.r <= r)
{
root.maxx = val;
root.tag = val;
return;
}
pushdown(u);
int mid = root.l + root.r >> 1;
if (l <= mid) modify(u << 1, l, r, val);
if (r > mid) modify(u << 1 | 1, l, r, val);
pushup(u);
}
double query(int u, int l, int r)
{
auto &root = tree[u];
if (root.l >= l && root.r <= r)
{
return root.maxx;
}
pushdown(u);
int mid = root.l + root.r >> 1;
double res = -inf;
if (l <= mid) res = query(u << 1, l, r);
if (r > mid) res = max(res, query(u << 1 | 1, l, r));
return res;
}
};
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> sum[i];
a[i] = sum[i];
sum[i] += sum[i - 1];
}
DSU dsuL(n + 1), dsuR(n + 1);
segmenttree smt(n);
smt.build(1, 1, n);
while (m--)
{
int op, l, r;
cin >> op >> l;
if (op == 1)
{
cin >> r;
l = dsuL.find(l);
r = dsuR.find(r);
double res = sum[r] - sum[l - 1];
double val = res / (r - l + 1);
smt.modify(1, l, r, val);
int low = dsuR.find(l);
int high = dsuL.find(r);
if (low < high)
{
for (int i = low + 1; i <= high; i++)
{
dsuL.merge(i - 1, i);
}
for (int i = high - 1; i >= low; i--)
{
dsuR.merge(i + 1, i);
}
}
}
else
{
cout << fixed << setprecision(10) << smt.query(1, l, l) << endl;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}
F题
思路
假设我们选中第 i i i个字符和第 j j j个字符( i < j i < j i<j)。想要比较 M o v e ( i ) Move(i) Move(i)和 M o v e ( j ) Move(j) Move(j)的大小,首先要看 s [ i ] s[i] s[i]和 s [ j ] s[j] s[j]的大小,如果相同,则需要继续比较 s [ i + 1 , j ] s[i+1,j] s[i+1,j]和 s [ i , j − 1 ] s[i,j-1] s[i,j−1]的字典序大小。
因此我们可以预处理出一个 N x t N o t E q u a l NxtNotEqual NxtNotEqual数组, N x t N o t E q u a l [ i ] NxtNotEqual[i] NxtNotEqual[i]表示从 i i i开始,最小的 j j j( j ≥ i j \ge i j≥i), s [ j ] s[j] s[j]和 s [ j + 1 ] s[j+1] s[j+1]不相同。
对于 i i i和 j j j,如果 N x t N o t E q u a l [ i ] ≥ j NxtNotEqual[i] \ge j NxtNotEqual[i]≥j则表示这段完全相同,否则比较 N x t N o t E q u a l [ i ] NxtNotEqual[i] NxtNotEqual[i]和 N x t N o t E q u a l [ i ] + 1 NxtNotEqual[i] +1 NxtNotEqual[i]+1的大小即可。
我们可以使用 s o r t sort sort函数,将上面的比较方式封装成一个lambda函数直接排序即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef pair<int, int> pii;
const int N = 1e6 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n, k;
int NxtNotEqual[N];
string s;
void solve()
{
cin >> n >> k >> s;
NxtNotEqual[n - 1] = n - 1;
for (int i = n - 2; i >= 0; i--)
{
NxtNotEqual[i] = NxtNotEqual[i + 1];
if (s[i] != s[i + 1])
NxtNotEqual[i] = i;
}
vector<int>v(n, 0);
iota(v.begin(), v.end(), 0);
sort(v.begin(), v.end(), [&](int x, int y) {
if (s[x] != s[y])
{
return s[x] < s[y];
}
if (x < y)
{
if (NxtNotEqual[x] >= y) return true;
return s[NxtNotEqual[x] + 1] < s[NxtNotEqual[x]];
}
else
{
if (NxtNotEqual[y] >= x) return true;
return s[NxtNotEqual[y]] < s[NxtNotEqual[y] + 1];
}
});
int idx = v[k - 1];
cout << s[idx];
for (int i = 0; i < n; i++)
{
if (i == idx) continue;
cout << s[i];
}
cout << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}
G题
思路
mex的取值为 [ 0 , 10 ] [0,10] [0,10],因此我们可以从大到小枚举mex,找到满足条件的mex。
根据题意,我们需要在 [ x , x + k ] [x,x+k] [x,x+k]中寻找满足条件的数字,因此我们考虑使用数位dp。
我们定义, d p [ p ] [ s t ] dp[p][st] dp[p][st],从高位起第 1 1 1位到第 p p p位,拥有的数字用 s t st st这个集合表示,会有 d p [ p ] [ s t ] dp[p][st] dp[p][st]个数字的mex是当前枚举的mex。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef pair<int, int> pii;
const int N = 10 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
int x, k;
int a[N], dp[N][(1ll << 10) + 5];
int dfs(int p, int limit, int lead, int st, int m)
{//p表示数字的位数。limit表示可以填数的限制,无限制的话0-9随便填。lead表示有无前导0,st表示状态的二进制数(0-1024)。
if (!p)
{
int mex = 0;
while (st >> mex & 1)
{
mex++;
}
return mex == m;
}
if (!limit && !lead && dp[p][st] != -1)
return dp[p][st];
int ans = 0;
int mx = limit ? a[p] : 9;//能枚举的数的上限
for (int i = 0; i <= mx; i++)
{
if (lead && i == 0)
{
ans += dfs(p - 1, limit && i == mx, lead && i == 0, st, m);
}
else
{
ans += dfs(p - 1, limit && i == mx, lead && i == 0, st | (1ll << i), m);
}
}
if (!limit && !lead)
{
dp[p][st] = ans;
}
return ans;
}
int calc(int x, int m)
{
int p = 0;
do
{
a[++p] = x % 10;
x /= 10;
} while (x);
memset(dp, -1, sizeof(dp));
return dfs(p, 1, 1, 0, m);
}
void solve()
{
cin >> x >> k;
int l = x, r = x + k;
for (int i = 10; i >= 0; i--)
{
int ans = calc(r, i) - calc(l - 1, i);
if (ans)
{
cout << i << " " << ans << endl;
return;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}