A.Floor Tiles
恶心构造,本来构造的方法没有错,因为不小心修改了第一块砖的位置,导致一直过不去,没注意,倒了
思路:先把最多曲线的构造出来,就是类似于
B
A
B
A
B
A
BABABA
BABABA或者
A
B
A
B
A
B
ABABAB
ABABAB的去构造,具体是哪一种形式取决于第一块瓷砖是
A
A
A还是
B
B
B,如果第一块为
A
A
A并且
x
x
x
y
y
y奇偶性一样就是
A
B
A
B
A
B
ABABAB
ABABAB这种, 否则
B
A
B
A
B
A
BABABA
BABABA,如果第一块为
B
B
B并且
x
x
x
y
y
y奇偶性一样就是
B
A
B
A
B
A
BABABA
BABABA这种, 否则
A
B
A
B
A
B
ABABAB
ABABAB,如图
红色部分就是旋转九十度以后会减少曲线的数量,每旋转一个就减少一条,所以我们构造出来以后,直接去计算红色部分的数量加上 n + m n + m n+m就是最多的曲线数,最少是 n + m n + m n+m,先把无解的情况判断了,在把需要减少的曲线数量算出来,记得不要修改题目所给地砖
void solve() {
int n, m, k; cin >> n >> m >> k;
int x, y; char t; cin >> x >> y >> t;
vector g(n + 1, vector<char>(m + 1));
if (t == 'A') {
if ((x & 1) == (y & 1)) g[1][1] = 'A';
else g[1][1] = 'B';
} else {
if ((x & 1) == (y & 1)) g[1][1] = 'B';
else g[1][1] = 'A';
}
for (int i = 2; i <= m; i ++) {
if (g[1][i - 1] == 'A') g[1][i] = 'B';
else g[1][i] = 'A';
}
for (int i = 2; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
if (g[i - 1][j] == 'A') g[i][j] = 'B';
else g[i][j] = 'A';
}
}
int smx = n + m, smn = n + m;
for (int i = 2; i <= n; i ++) {
for (int j = 1; j < m; j ++) {
if (g[i][j] == 'B') {
smx ++;
}
}
}
if (k < smn || k > smx) {
cout << "No\n";
return ;
}
int cnt = smx - k;
if (t == 'A') {
for (int i = 2; i <= n && cnt > 0; i ++) {
for (int j = 1; j < m && cnt > 0; j ++) {
if (g[i][j] == 'B') {
g[i][j] = 'A';
cnt --;
}
}
}
} else {
for (int i = 2; i <= n && cnt > 0; i ++) {
for (int j = 2; j <= m && cnt > 0; j ++) {
if (g[i][j] == 'A') {
g[i][j] = 'B';
cnt --;
}
}
}
}
cout << "Yes\n";
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
cout << g[i][j];
}
cout << '\n';
}
}
B.MST
思路:根号分治,小于等于 n \sqrt{n} n 的直接 O ( n 2 ) O(n^2) O(n2)把需要的边选出来做克鲁斯卡尔,否则直接对所有的边做克鲁斯卡尔
struct S {
int u, v, w;
};
vector<S> val;
map<array<int, 2>, int> g;
int n, m, q;
int fa[N];
int find(int x) {
if (fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
void merge(int a, int b) {
int pa = find(a), pb = find(b);
fa[pa] = pb;
}
void solve() {
cin >> n >> m >> q;
val.resize(m + 1);
for (int i = 1; i <= m; i ++) {
int a, b, c; cin >> a >> b >> c;
val[i] = {a, b, c};
g[ {a, b}] = c;
g[ {b, a}] = c;
}
sort(val.begin() + 1, val.end(), [&](S a, S b) {
return a.w < b.w;
});
while (q --) {
int k; cin >> k;
vector<int> node(k + 1), query(n + 1);
for (int i = 1; i <= k; i ++) {
cin >> node[i];
fa[node[i]] = node[i];
query[node[i]] = 1;
}
vector<S> edge;
int cnt = 0, sum = 0;
if (k < 1000) {
for (int i = 1; i <= k; i ++) {
for (int j = i + 1; j <= k; j ++) {
if (i == j) continue;
int u = node[i], v = node[j];
if (g.count({u, v})) {
edge.push_back({u, v, g[{u, v}]});
}
}
}
sort(edge.begin(), edge.end(), [&](S a, S b) {
return a.w < b.w;
});
for (int i = 0; i < edge.size(); i ++) {
auto [u, v, w] = edge[i];
int pu = find(u), pv = find(v);
if (pu != pv) {
cnt ++;
sum += w;
merge(u, v);
}
}
if (cnt < k - 1) cout << -1 << '\n';
else cout << sum << '\n';
} else {
for (int i = 1; i <= m; i ++) {
auto [u, v, w] = val[i];
int pu = find(u), pv = find(v);
if (pu != pv && query[u] && query[v]) {
cnt ++;
sum += w;
merge(u, v);
}
}
if (cnt < k - 1) cout << -1 << '\n';
else cout << sum << '\n';
}
}
}
C.Red Walking on Grid
思路:直接 d p dp dp, d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1]在第 i i i走第一行和第二行的最大步数,优先让左边的先转移,在转移上下的,去 m a x max max即可
void solve() {
int n; cin >> n;
string s1, s2; cin >> s1 >> s2;
vector<array<int, 2>> dp(n + 1);
int ans = 0;
if (s1[0] == s2[0] && s1[0] == 'R') {
dp[0][0] = dp[0][1] = 1, ans = 1;
}
for (int i = 1; i < s1.size(); i++) {
if (s1[i] == 'R' && s1[i - 1] == 'R') {
dp[i][0] = max(dp[i - 1][0] + 1, dp[i][0]);
}
if (s2[i] == 'R' && s2[i - 1] == 'R') {
dp[i][1] = max(dp[i][1], dp[i - 1][1] + 1);
}
if (s1[i] == s2[i] && s1[i] == 'R') {
int t0 = dp[i][0], t1 = dp[i][1];
dp[i][0] = max(dp[i][0], t1 + 1);
dp[i][1] = max(dp[i][1], t0 + 1);
}
ans = max(ans, max(dp[i][1], dp[i][0]));
}
cout << ans << '\n';
}
E.GCD VS XOR
思路:取二进制下从低位开始的第一个 1 1 1的位置和 n n n做 g c d gcd gcd即可,二的整次幂没有答案
void solve() {
int n; cin >> n;
for (int i = 0; i < 64; i ++) {
if (n >> i & 1) {
int x = (1ll << i);
if (x != n) {
cout << (n ^ x) << '\n';
return ;
}
}
}
cout << -1 << '\n';
}
H.Instructions Substring
思路:首先特判目标点在 0 , 0 {0, 0} 0,0的情况,答案就是 n ⋅ ( n + 1 ) 2 \frac{n \cdot (n + 1)}{2} 2n⋅(n+1),剩下的情况我一边遍历一边记录在这个位置我的坐标偏移到了那个位置,我把和目标点的位置差多少算出来,而这个差值我要让我减去前缀的贡献,记得要把这个前缀清零不然后续会重复计算
void solve() {
int n, x, y; cin >> n >> x >> y;
string s; cin >> s;
int X = 0, Y = 0;
map<pair<int, int>, int> mp;
if (x == 0 && y == 0) {
cout << n * (n + 1) / 2 << '\n';
return ;
}
int ans = 0;
mp[ {0, 0}] ++;
for (int i = 0; i < n; i ++) {
if (s[i] == 'A') X --;
else if (s[i] == 'D') X ++;
else if (s[i] == 'W') Y ++;
else Y --;
int dx = X - x, dy = Y - y;
ans += mp[ {dx, dy}] * (n - i);
mp[ {dx, dy}] = 0;
mp[ {X, Y}] ++;
}
cout << ans << '\n';
}
I.Red Playing Cards
思路:其实感觉这个题更像线性的 d p dp dp, f [ i ] f[i] f[i]表示进行删除操作以后的最大价值是多少,因为只有首位一样才可以删,所以对于每一对首位相同的区间我们进行 d p dp dp, d p [ i ] dp[i] dp[i]表示到第 i i i个位置我的最大价值是多少,只有当我的一个区间完全包含在现在遍历的区间之内的时候,我才可能会考虑删还是不删,取 m a x max max即可
void solve() {
int n; cin >> n;
vector<int> a(n * 2 + 2);
unordered_map<int, array<int, 2>> mp;
for (int i = 1; i <= n * 2; i ++) {
cin >> a[i];
if (!mp.count(a[i])) {
mp[a[i]][0] = i;
} else {
mp[a[i]][1] = i;
}
}
vector<array<int, 3>> seg;
for (auto [x, y] : mp) {
seg.push_back({x, y[0], y[1]});
}
seg.push_back({0, 0, 2 * n + 1});
sort(seg.begin(), seg.end(), [&](array<int, 3> a, array<int, 3> b) {
return (a[2] - a[1]) < (b[2] - b[1]);
});
vector<int> f(n + 1);
for (auto [val, l, r] : seg) {
vector<int> dp(r + 1, 0);
dp[l] = val;
for (int i = l + 1; i <= r; i ++) {
int lo = mp[a[i]][0], hi = mp[a[i]][1];
dp[i] = max(dp[i], dp[i - 1] + val);
if (lo > l && hi < r && i >= hi) dp[i] = max(dp[i], dp[lo - 1] + f[a[i]]);
}
f[val] = dp[r];
}
cout << f[0] << '\n';
}
标签:int,max,++,多校,cin,2024,牛客,mp,dp
From: https://blog.csdn.net/weixin_73914071/article/details/141062096