Codeforces Round #798 (Div. 2)
[Link](Dashboard - Codeforces Round #798 (Div. 2) - Codeforces)
A. Lex String
思路
- 贪心
前面的字符具有决定性,因此贪心来看直接对 a , b a, b a,b排序后,类似于归并排序每次选满足限制的最小的即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n >> m >> k;
string a, b; cin >> a >> b;
sort(a.begin(), a.end());
sort(b.begin(), b.end());
string res = "";
int pa = 0, pb = 0, ida = 0, idb = 0;
for (int i = 0; i < a.size() + b.size(); i ++) {
if (ida < a.size() && idb < b.size()) {
if (a[ida] < b[idb]) {
if (pa < k) {
pb = 0, pa ++;
res += a[ida ++];
}
else {
pa = 0, pb ++;
res += b[idb ++];
}
}
else {
if (pb < k) {
pa = 0, pb ++;
res += b[idb ++];
}
else {
pb = 0, pa ++;
res += a[ida ++];
}
}
}
else break;
}
cout << res << '\n';
}
return 0;
}
B. Mystic Permutation
思路
- 贪心
排序后,每次找到当前可以的最小的,要特别注意有能最后一个数剩下的是他本身,因此最后两位特判一下即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N], res[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
if (n == 1) {
cout << - 1 << '\n';
continue ;
}
if (n == 2) {
cout << a[2] << ' ' << a[1] << '\n';
continue ;
}
vector<bool> st(n + 1);
for (int i = 1; i <= n - 2; i ++) {
int k = 1;
while (st[k] || k == a[i]) k ++;
res[i] = k;
st[k] = true;
}
int aa, bb;
int k = 1;
while (st[k]) k ++;
aa = k, st[k] = true, k = 1;
while (st[k]) k ++;
bb = k;
if (aa == a[n - 1] || a[n] == bb) res[n - 1] = bb, res[n] = aa;
else res[n - 1] = aa, res[n] = bb;
for (int i = 1; i <= n; i ++) cout << res[i] << " \n"[i == n];
}
return 0;
}
C. Infected Tree
思路
- 树形 d p dp dp
观察发现对于 u u u,假设他有两个子结点 s 1 , s 2 s_1,s_2 s1,s2,因此只有两种选择,砍 s 1 s_1 s1烧 s 2 s_2 s2或者砍 s 2 s_2 s2烧 s 1 s_1 s1,由于无法判断哪种情况更优,因此考虑 d p dp dp
设 f [ u ] : u 点 正 在 燃 烧 且 以 u 为 根 的 子 树 能 留 下 的 最 大 值 f[u]:u点正在燃烧且以u为根的子树能留下的最大值 f[u]:u点正在燃烧且以u为根的子树能留下的最大值, s i z e [ u ] : 以 u 为 根 的 子 树 的 结 点 数 size[u]:以u为根的子树的结点数 size[u]:以u为根的子树的结点数,然后直接递归转移每次两种情况里取最优解,即 f [ u ] = m a x ( s i z e [ s 1 ] − 1 + f [ s 2 ] , s i z e [ s 2 ] − 1 + f [ s 1 ] ) f[u]=max(size[s_1] - 1 + f[s_2],size[s_2]-1+f[s_1]) f[u]=max(size[s1]−1+f[s2],size[s2]−1+f[s1])。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 3e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N], f[N];
void dfs(int u, int fa) {
a[u] = 1;
vector<int> ve;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue ;
dfs(j, u);
ve.push_back(j);
a[u] += a[j];
}
if (ve.size() == 1) f[u] = a[ve[0]] - 1;
else if (ve.size() == 2) f[u] = max(a[ve[0]] + f[ve[1]] - 1, a[ve[1]] + f[ve[0]] - 1);
else f[u] = 0;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
for (int i = 1; i <= n; i ++) h[i] = -1, f[i] = 0;
for (int i = 1; i < n; i ++) {
int x, y; cin >> x >> y;
add(x, y), add(y, x);
}
dfs(1, -1);
cout << f[1] << '\n';
}
return 0;
}
D. Lena and Matrix
思路
- 枚举, d p dp dp
一个很暴力的想法是枚举每个位置再枚举每个黑棋子求当前情况的 m x mx mx,所有情况再取个 m n mn mn。很明显这样的复杂度是很高的,换一个角度来枚举,对于 ( i , j ) (i,j) (i,j)这个点我们只有四种情况,上下左右去找黑棋子,设 f [ i ] [ j ] : ( i , j ) 到 最 远 黑 色 格 子 的 距 离 f[i][j]:(i,j)到最远黑色格子的距离 f[i][j]:(i,j)到最远黑色格子的距离,它可以从上下左右四个方向转移过来,这样其实是不好一下转移的,我们进行分类讨论,对于 ( i , j − 1 ) (i,j-1) (i,j−1)可以是从左上角到 ( i , j − 1 ) (i,j-1) (i,j−1)或左下角到 ( i , j − 1 ) (i,j-1) (i,j−1),其他三个方向同理,因此我们可以分别从四个角落开始往对角 d p dp dp,然后对于每个位置的最大值这四种情况取 m a x max max。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
char g[1010][1010];
int d[4][1010][1010];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n >> m;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++) {
cin >> g[i][j];
for (int k = 0; k < 4; k ++) d[k][i][j] = -1;
}
idx = 0;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++) {
if (g[i][j] == 'B') d[idx][i][j] = max(d[idx][i][j], 0);
if (~d[idx][i][j]) d[idx][i + 1][j] = max(d[idx][i + 1][j], d[idx][i][j] + 1), d[idx][i][j + 1] = max(d[idx][i][j + 1], d[idx][i][j] + 1);
}
idx ++;
for (int i = 1; i <= n; i ++)
for (int j = m; j; j --) {
if (g[i][j] == 'B') d[idx][i][j] = max(d[idx][i][j], 0);
if (~d[idx][i][j]) d[idx][i + 1][j] = max(d[idx][i + 1][j], d[idx][i][j] + 1), d[idx][i][j - 1] = max(d[idx][i][j - 1], d[idx][i][j] + 1);
}
idx ++;
for (int i = n; i; i --)
for (int j = 1; j <= m; j ++) {
if (g[i][j] == 'B') d[idx][i][j] = max(d[idx][i][j], 0);
if (~d[idx][i][j]) d[idx][i - 1][j] = max(d[idx][i - 1][j], d[idx][i][j] + 1), d[idx][i][j + 1] = max(d[idx][i][j + 1], d[idx][i][j] + 1);
}
idx ++;
for (int i = n; i; i --)
for (int j = m; j; j --) {
if (g[i][j] == 'B') d[idx][i][j] = max(d[idx][i][j], 0);
if (~d[idx][i][j]) d[idx][i - 1][j] = max(d[idx][i - 1][j], d[idx][i][j] + 1), d[idx][i][j - 1] = max(d[idx][i][j - 1], d[idx][i][j] + 1);
}
LL res = INF;
PII t;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++) {
int v = -1;
for (int k = 0; k < 4; k ++)
v = max(v, d[k][i][j]);
if (v < res) res = v, t = {i, j};
}
//cout << res << '\n';
cout << t.x << ' ' << t.y << '\n';
}
return 0;
}
E. ANDfinity
思路
- 位运算,并查集
由于 & > 0 \&>0 &>0的点可以连边,如果某个数是 0 0 0则一定要对其加一,并且如果两个点权都是奇数一定可以连边因为他们二进制第零位一定都是 1 1 1,对于其他的数就不一定了。这里有一个结论是当数组均非零后,再做的操作最多是2。
证明这个结论,设 l o w b i t ( x ) : x 二 进 制 从 低 往 高 第 一 个 为 1 的 位 置 , m x = m a x ( l o w b i t ( a 1 ) , . . . , l o w b i t ( a n ) ) lowbit(x):x二进制从低往高第一个为1的位置,mx=max(lowbit(a_1),...,lowbit(a_n)) lowbit(x):x二进制从低往高第一个为1的位置,mx=max(lowbit(a1),...,lowbit(an)),分类来看如果本身就连通则操作为0,如果不连通且有一个 m x mx mx,我们将 m x mx mx这个数减一那么这个数就可以和所有的数相连,图就联通了,如果有大于等于两个 m x mx mx存在,我们任选两个,一个加一一个减一,这样所有的也连通了,因此操作最多两次。
查看是否连通可以用并查集来做,然后按结论分类来试即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N], f[N];
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
if (x != y)
f[x] = y;
}
bool check() {
for (int i = 1; i <= n + 31; i ++) f[i] = i;
for (int i = 1; i <= n; i ++)
for (int j = 0; j <= 30; j ++)
if (a[i] >> j & 1)
merge(n + j + 1, i);
int cnt = 0;
for (int i = 1; i <= n; i ++)
if (i == f[i])
cnt ++;
return cnt == 1;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
int res = 0;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
if (!a[i]) a[i] ++, res ++;
}
if (check()) {
cout << res << '\n';
for (int i = 1; i <= n; i ++)
cout << a[i] << " \n"[i == n];
continue ;
}
bool ok = false;
for(int i = 1; i <= n; i ++) {
a[i] ++;
if (check()) {
cout << res + 1 << '\n';
for (int i = 1; i <= n; i ++)
cout << a[i] << " \n"[i == n];
ok = true;
break;
}
a[i] --;
}
if (ok) continue;
for (int i = 1; i <= n; i ++) {
a[i] --;
if (check()) {
cout << res + 1 << '\n';
for (int i = 1; i <= n; i ++)
cout << a[i] << " \n"[i == n];
ok = true;
break;
}
a[i] ++;
}
if (ok) continue;
int mx = 0;
for (int i = 1; i <= n; i ++) mx = max(mx, a[i] & -a[i]);
for (int i = 1; i <= n; i ++)
if (mx == (a[i] & -a[i])) {
a[i] ++;
break;
}
for (int i = 1; i <= n; i ++)
if (mx == (a[i] & -a[i])) {
a[i] --;
break;
}
cout << rs + 2 << '\n';
for (int i = 1; i <= n; i ++)
cout << a[i] << " \n"[i == n];
}
return 0;
}