Codeforces Round 854 by cybercats (Div. 1 + Div. 2)
C. Double Lexicographically Minimum
题意:给你一个字符串 ,你可以重排 中的字母而得到字符串 。定义 为 和 ( 是 前后翻转得到的字符串)中字典序较大者。现在你需要让 的字典序最小,并输出 。
题解:
考虑构造,显然可以从小到大枚举 的所有字符,设当前枚举到 :
1、若 还剩下至少两个,则将答案的前缀和后缀各加上 ;
2、若除 外只剩下一种字符 ,设其出现次数为 ,则将前缀加上 个 ,加上 ,再加上 个 ;
3、否则将剩下的、不包括 的所有字符按字典序加入前缀,再加入 。
具体代码如下:
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <set>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#define int long long
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PII;
const int N = 100010, M = 5010;
const int Mod = 1e9 + 7;
const int INF = 1e18;
int n, T, m, k;
string a;
char b[N];
int tmp[30];
void solve()
{
memset(tmp, 0, sizeof tmp);
cin >> a;
n = a.length();
for (auto i : a) tmp[i - 'a'] ++ ;
int backup = -1;
int l = 0, r = n - 1;
for (int i = 0; i < 26; i ++ )
{
while (tmp[i] > 1)
{
b[l ++ ] = b[r -- ] = i + 'a';
tmp[i] -= 2;
}
if (tmp[i])
{
backup = i;
break;
}
}
if (~backup)
{
int f = 0;//置1为后面只剩v一个字母,置0反之
for (int i = backup + 1; i < 26; i ++ )
{
if (tmp[i])
{
if (tmp[i] + 1 == r - l + 1) f = 1;
break;
}
}
if (f)
{
for (int i = 0; i < 26; i ++ )
while (tmp[i] > 1)
b[l ++ ] = b[r -- ] = i + 'a', tmp[i] -= 2;
}
b[r -- ] = backup + 'a', tmp[backup] -- ;
for (int i = 0; i < 26; i++)
while (tmp[i])
b[l ++ ] = i + 'a', tmp[i] -- ;
}
for (int i = 0; i < n; i ++ ) cout << b[i];
cout << endl;
}
signed main()
{
// quick_cin();
cin >> T;
// getchar();
// T = 1;
while (T -- )
{
solve();
}
return 0;
}
D1. Hot Start Up (easy version)
题意:你有两个 CPU,n 个程序要运行,总共 k 种程序需要运行。程序按顺序进行,每次执行程序如果你选择的 CPU 上一次运行的程序不是你当前运行的程序,则需要花费 的时间。否则,需要花费 的时间。
题解:在本题当中按照题意本应该定义 表示放完第 个物品并且第一条序列末尾是 并且第二条序列末尾是 时最小的花费,这样会变成 的时间复杂度,但是放完第 个物品时就可以知道其中一个的末尾元素了,即 ,因此数组变成 ,表示放完第 个物品后其中一条序列后另一条子序列的结尾是 时的最小花费。
此时的状态转移方程:
第一种——放在第 个元素的后面
f[i][j] = min(f[i][j], f[i - 1][j] + (a[i] == a[i - 1] ? hot[a[i]] : cold[a[i]]));
第二种——不放在第 个元素的后面,此时另一条自序列的末尾肯定是
f[i][a[i - 1]] = min(f[i][a[i - 1]], f[i - 1][j] + (a[i] == j ? hot[a[i]] : cold[a[i]]));
具体代码如下:
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <set>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#define int long long
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PII;
const int N = 5010, M = 5010;
const int Mod = 1e9 + 7;
const int INF = 1e18;
int n, T, m, k;
int res;
int f[N][N];//表示放完第i个物品并且另一个CPU最后程序是j的最小花费
int a[N], hot[N], cold[N];
void solve()
{
cin >> n >> k;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 1; i <= k; i ++ ) cin >> cold[i];
for (int i = 1; i <= k; i ++ ) cin >> hot[i];
for (int i = 0; i <= n; i ++ )
for (int j = 0; j <= k; j ++ )
f[i][j] = INF;
f[1][0] = cold[a[1]];
for (int i = 2; i <= n; i ++ )
{
int last = a[i - 1];
for (int j = 0; j <= k; j ++ )
{
//放在第i-1个程序后面
f[i][j] = min(f[i][j], f[i - 1][j] + (a[i] == last ? hot[a[i]] : cold[a[i]]));
//不放在第i-1个程序后面
f[i][last] = min(f[i][last], f[i - 1][j] + (a[i] == j ? hot[a[i]] : cold[a[i]]));
}
}
res = INF;
for (int i = 0; i <= k; i ++ )
res = min(res, f[n][i]);
cout << res << endl;
}
signed main()
{
quick_cin();
cin >> T;
// getchar();
// T = 1;
while (T -- )
{
solve();
}
return 0;
}
D2. Hot Start Up (hard version)
题意:在上一题的基础上扩大了 和 的取值范围,此时 会TLE。
题解:观察之前的二维 DP , 与 (此时反过来好思考一点)之间的转移也分为两种情况(与之前的两个情况相对应):
1、对于所有的 等于 加上 或者 (由 是否等于 决定);
2、此外,对于 还可以取 。
具体代码如下:
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <set>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#define int long long
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PII;
const int N = 300010, M = 5010;
const int Mod = 1e9 + 7;
const int INF = 1e18;
int n, T, m, k;
int res;
int a[N], hot[N], cold[N];
struct Node
{
int l, r;
int v; // 区间[l, r]中的最小值
int add;
}tr[N * 4];
void pushup(int u)
{
tr[u].v = min(tr[u << 1].v, tr[u << 1 | 1].v);
}
void pushdown(int u)
{
auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
if (root.add)
{
left.add += root.add, left.v += root.add;
right.add += root.add, right.v += root.add;
root.add = 0;
}
}
void build(int u, int l, int r)
{
tr[u] = {l, r};
if (l == r)
{
tr[u].v = INF;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
int query(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].v;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
int v = INF;
if (l <= mid) v = query(u << 1, l, r);
if (r > mid) v = min(v, query(u << 1 | 1, l, r));
return v;
}
void modify(int u, int p, int val)//单点取min
{
if (tr[u].l == p && tr[u].r == p)
{
tr[u].v = min(tr[u].v, val);
return;
}
else
{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (mid >= p) modify(u << 1, p, val);
else modify(u << 1 | 1, p, val);
pushup(u);
}
}
void modify(int u, int l, int r, int d)//区间加
{
if (tr[u].l >= l && tr[u].r <= r)
{
tr[u].v += (LL)(tr[u].r - tr[u].l + 1) * d;
tr[u].add += d;
}
else // 一定要分裂
{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, d);
if (r > mid) modify(u << 1 | 1, l, r, d);
pushup(u);
}
}
void solve()
{
cin >> n >> k;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 1; i <= k; i ++ ) cin >> cold[i];
for (int i = 1; i <= k; i ++ ) cin >> hot[i];
build(1, 0, k);
res = INF;
modify(1, 0, 0);
for (int i = 1; i <= n; i ++ )
{
res = min(query(1, 0, k) + cold[a[i]], query(1, a[i], a[i]) + hot[a[i]]);//区间查询
modify(1, 0, k, a[i] == a[i - 1] ? hot[a[i]] : cold[a[i]]);//区间加
modify(1, a[i - 1], res);//单点修改
}
printf("%lld\n", query(1, 0, k));
}
signed main()
{
quick_cin();
cin >> T;
// getchar();
// T = 1;
while (T -- )
{
solve();
}
return 0;
}
E. City Union
题意:给你一个 n × m 的网格图,每个点为 '#' 或 '.'。定义一个城市为一个 '#' 的连通块。一开始原图中有两个城市,要求你用最少次将某个格子变为 '#' 的操作使得图中只有一个城市,满足其中任意两个 '#' 的只经过 '#' 的最短路长度为它们的曼哈顿距离(在两点 (a, b) 和 (c, d) 之间的曼哈顿距离为 )。
题解:
首先考虑满足第二个条件,发现如果有两个 '#' 之间的最短路会绕成 C 形则不满足。于是发现每一行每一列都必须为一段连续的 '#',否则断开的两段中任意两点的最短路不满足要求。
于是需要将每一行每一列填满成一个连续段。
然后还有可能是两个城市,我们需要将它们连在一起,应该各取一个坐标为已填了的点中最大(小)横坐标和最大(小)纵坐标的点,随便连一条最短路,然后再填满。
可以发现这样做需要填的格子数量是最小的。
具体代码如下:
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <set>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#define int long long
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PII;
const int N = 110, M = 5010;
const int Mod = 1e9 + 7;
const int INF = 1e18;
int n, T, m, k;
int res;
multiset<int> S;
char dist[N][N];
int l[N], r[N];
void solve()
{
S.clear();
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
{
l[i] = 51, r[i] = 0;
for (int j = 1; j <= m; j ++ )
{
cin >> dist[i][j];
if (dist[i][j] == '#')
{
r[i] = j, S.insert(j);
if (l[i] > 50) l[i] = j;
}
}
}
for (int i = 1; i <= n; i ++ )
if (l[i] < 51)
{
for (int j = l[i]; j <= r[i]; j ++ )
{
if (dist[i][j] == '.')
dist[i][j] = '#';
else
S.erase(S.find(j));
}
if (!S.empty())
{
if (*S.begin() < l[i])//防止产生向左的谷底
l[i + 1] = min(l[i + 1], l[i]);
else//防止两个联通块之间不连接
l[i + 1] = min(r[i], *S.begin());
if (*S.rbegin() > r[i])//防止产生向右的谷底
r[i + 1] = max(r[i + 1], r[i]);
else//防止两个联通块之间不连接
r[i + 1] = max(l[i], *S.rbegin());
}
}
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ )
cout << dist[i][j];
cout << endl;
}
}
signed main()
{
quick_cin();
cin >> T;
// getchar();
// T = 1;
while (T -- )
{
solve();
}
return 0;
}