【比赛链接】
【题解链接】
**【A】**String Game
【思路要点】
- 显然可以二分答案。
- 然后判定 是否为删减后的 的子序列即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, m, a[MAXN];
char s[MAXN], t[MAXN];
bool flg[MAXN];
bool check(int mid) {
memset(flg, 0, sizeof(flg));
for (int i = 1; i <= mid; i++)
flg[a[i]] = true;
int pos = 1;
for (int i = 1; i <= m; i++) {
while (pos <= n && !(!flg[pos] && s[pos] == t[i])) pos++;
if (pos > n) return false; pos++;
}
return true;
}
int main() {
scanf("\n%s\n%s", s + 1, t + 1);
n = strlen(s + 1);
m = strlen(t + 1);
for (int i = 1; i <= n; i++)
read(a[i]);
int l = 0, r = n - m;
while (l < r) {
int mid = (l + r + 1) / 2;
if (check(mid)) l = mid;
else r = mid - 1;
}
writeln(l);
return 0;
}
**【B】**Bitwise Formula
【思路要点】
- 容易发现问题对于每一位是独立的。
- 对于每一位,枚举其取值 ,模拟一遍题目中的过程,取较最优者作为答案。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, m, now[MAXN];
int x[MAXN], y[MAXN];
string name, rubbish, Min, Max;
string opt[MAXN], val[MAXN];
map <string, int> num;
int main() {
read(n), read(m);
for (int i = 1; i <= n; i++) {
cin >> name;
num[name] = i;
cin >> rubbish;
cin >> val[i];
if (val[i][0] == '0' || val[i][0] == '1') continue;
else {
x[i] = num[val[i]];
cin >> opt[i];
cin >> rubbish;
y[i] = num[rubbish];
}
}
for (int j = 0; j < m; j++) {
int sumz = 0, sumo = 0;
now[0] = 0;
for (int i = 1; i <= n; i++) {
if (opt[i] == "OR") now[i] = now[x[i]] | now[y[i]];
else if (opt[i] == "AND") now[i] = now[x[i]] & now[y[i]];
else if (opt[i] == "XOR") now[i] = now[x[i]] ^ now[y[i]];
else now[i] = val[i][j] - '0';
sumz += now[i];
}
now[0] = 1;
for (int i = 1; i <= n; i++) {
if (opt[i] == "OR") now[i] = now[x[i]] | now[y[i]];
else if (opt[i] == "AND") now[i] = now[x[i]] & now[y[i]];
else if (opt[i] == "XOR") now[i] = now[x[i]] ^ now[y[i]];
else now[i] = val[i][j] - '0';
sumo += now[i];
}
if (sumz <= sumo) Min += '0';
else Min += '1';
if (sumz >= sumo) Max += '0';
else Max += '1';
}
cout << Min << endl;
cout << Max << endl;
return 0;
}
**【C】**Peterson Polyglot
【思路要点】
我们本质上希望求出的是对于每一个点 ,若将其所有儿子全部删除,将其孙子连接至其下并整理(合并相同的字符)后会消失的节点数 ,如果得到了 ,那么我们只需要找到 的总和最大的一层节点,将它们的儿子删去即可。
考虑一个直观的做法:
对于每个点 ,首先令 为其儿子数。
考虑整理字典树的过程,将其儿子中所有带有出边 的点找到,令这样的点数为 ,若 ,将 增加 ,并递归整理这些点的 出边指向的节点。
考虑其复杂度,可以发现,该算法的整理部分涉及到的总点数为 。
并且在点 处被整理的点数不会超过以点 为根的子树大小减去点 最大的子树的大小。
也就是说,一个点一旦在 中被计算一次,计算它的子树大小就会至少翻倍。
因此,一个点在 中被计算的次数为 次,该算法的整理部分涉及到的总点数为 。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
const int MAXC = 26;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
struct edge {int dest; char val; };
vector <edge> a[MAXN];
vector <int> p[MAXN];
int n, benifit[MAXN];
int child[MAXN][MAXC], depth[MAXN];
int calc(int depth) {
if (p[depth].size() <= 1) return 0;
int ans = 0;
for (int j = 0; j < 26; j++) {
p[depth + 1].clear();
int cnt = 0;
for (unsigned i = 0; i < p[depth].size(); i++)
if (child[p[depth][i]][j]) {
cnt++;
p[depth + 1].push_back(child[p[depth][i]][j]);
}
ans += max(cnt - 1, 0) + calc(depth + 1);
}
return ans;
}
void dfs(int pos, int fa) {
depth[pos] = depth[fa] + 1;
for (unsigned i = 0; i < a[pos].size(); i++)
if (a[pos][i].dest != fa) {
child[pos][a[pos][i].val - 'a'] = a[pos][i].dest;
dfs(a[pos][i].dest, pos);
}
}
int main() {
read(n);
for (int i = 1; i <= n - 1; i++) {
int x, y; char z;
scanf("%d%d %c", &x, &y, &z);
a[x].push_back((edge) {y, z});
a[y].push_back((edge) {x, z});
}
dfs(1, 0);
for (int i = 1; i <= n; i++) {
p[0].clear();
for (int j = 0; j < MAXC; j++)
if (child[i][j]) {
benifit[depth[i]]++;
p[0].push_back(child[i][j]);
}
benifit[depth[i]] += calc(0);
}
int ans = 1;
for (int i = 1; i <= n; i++)
if (benifit[i] > benifit[ans]) ans = i;
writeln(n - benifit[ans]);
writeln(ans);
return 0;
}
**【D】**Parquet Re-laying
【思路要点】
我们发现难以轻易地构造出一组无解的情况,因此我们猜想所有合法的状态都是能够相互到达的。
由于操作可逆,我们可以试图将给出的两个状态分别归一与一个统一的中间状态。
和 必然至少有一个是偶数,不妨令 为偶数( 为偶数的情况是对称的),我们希望将每个状态归一于所有地板竖直摆放的状态。
考虑最左边的列,若所有地板已经竖直摆放,那么将这列删去(不予考虑)
否则,最左边的列可能包含一些水平摆放的地板的左半段,并且由于 是偶数,这样的左半段的个数也为偶数,它们两两之间由竖直摆放的地板连接,相隔的距离也为偶数。
若一对竖直对齐的水平摆放的地板中间(两列)仅有竖直摆放的地板,那么显然我们可以将这些地板通过操作变为水平摆放的,再将这一段内的所有地板通过操作变为竖直摆放的,这样就消除了一对竖直对齐的水平摆放的地板,使用的操作次数是 的。
可以证明若当前还存在水平摆放的地板,那么一定存在上述的一对竖直对齐的水平摆放的地板,使得它们中间仅有竖直摆放的地板。证明较为显然,因为一对竖直对齐的水平摆放的地板中间若存在水平摆放的地板,一定可以找到一对距离更近的竖直对齐的水平摆放的地板。
那么算法就很明显了,暴力找到上述的一对竖直对齐的水平摆放的地板,花费 的操作次数消除它们,重复执行直到不存在水平摆放的地板。
执行次数至多 ,单次花费时间至多 。
时间复杂度 ,使用操作次数上限 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, m;
char s[MAXN][MAXN];
vector <int> ansx[2], ansy[2];
void getplan(vector <int> &ansx, vector <int> &ansy) {
if (n % 2 == 0) {
int cnt = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cnt += s[i][j] == 'L';
while (cnt != 0) {
int px = 0, py = 0, qx = 0, qy = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (s[i][j] == 'L') {
bool flg = false;
for (int k = i + 1; k <= n; k++)
if (s[k][j] == 'L') {
flg = true;
qx = k, qy = j;
break;
} else if (s[k][j] == 'R' || s[k][j + 1] == 'L') break;
if (flg) px = i, py = j;
}
if (px == 0) {
printf("Error!\n");
exit(0);
}
for (int i = px + 1; i <= qx - 1; i += 2) {
ansx.push_back(i);
ansy.push_back(py);
}
for (int i = px; i <= qx; i += 2) {
ansx.push_back(i);
ansy.push_back(py);
s[i][py] = s[i][py + 1] = 'U';
s[i + 1][py] = s[i + 1][py + 1] = 'D';
}
cnt -= 2;
}
} else {
int cnt = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cnt += s[i][j] == 'U';
while (cnt != 0) {
int px = 0, py = 0, qx = 0, qy = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (s[i][j] == 'U') {
bool flg = false;
for (int k = j + 1; k <= m; k++)
if (s[i][k] == 'U') {
flg = true;
qx = i, qy = k;
break;
} else if (s[i][k] == 'D' || s[i + 1][k] == 'U') break;
if (flg) px = i, py = j;
}
if (px == 0) {
printf("Error!\n");
exit(0);
}
for (int i = py + 1; i <= qy - 1; i += 2) {
ansx.push_back(px);
ansy.push_back(i);
}
for (int i = py; i <= qy; i += 2) {
ansx.push_back(px);
ansy.push_back(i);
s[px][i] = s[px + 1][i] = 'L';
s[px][i + 1] = s[px + 1][i + 1] = 'R';
}
cnt -= 2;
}
}
}
int main() {
read(n), read(m);
for (int i = 1; i <= n; i++)
scanf("\n%s", s[i] + 1);
getplan(ansx[0], ansy[0]);
for (int i = 1; i <= n; i++)
scanf("\n%s", s[i] + 1);
getplan(ansx[1], ansy[1]);
writeln(ansx[0].size() + ansx[1].size());
for (unsigned i = 0; i < ansx[0].size(); i++)
printf("%d %d\n", ansx[0][i], ansy[0][i]);
reverse(ansx[1].begin(), ansx[1].end());
reverse(ansy[1].begin(), ansy[1].end());
for (unsigned i = 0; i < ansx[1].size(); i++)
printf("%d %d\n", ansx[1][i], ansy[1][i]);
return 0;
}
**【E】**Selling Numbers
【思路要点】
- 由于要考虑进位的问题,我们考虑从低位向高位DP。
- 我们发现无论如何,我们加上的数是对于每个 相同的,因此 相对的大小关系不会产生变化。
- 那么,无论我们加上什么数,从第 位向第 位有进位的数一定是第 位到第一位组成的数较大的一些数,因此,对于每一位,可能的产生进位的集合只有 个。
- 记 表示已经考虑了所有数较低的 位,其中有 个数产生了进位时,能够获得的最大价值。
- 记 为一层,从 到 之间的转移我们可以较为简单地做到 的时间复杂度,完成一层的转移后,对所有数第 位到第一位组成的数重新排序即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
const int INF = 1e9;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, m, Max, type, val[MAXN], len[MAXN], home[MAXN];
int a[MAXN][MAXN], b[MAXN], dp[MAXN][MAXN];
char s[MAXN];
bool cmp(int x, int y) {
if (a[x][type] == a[y][type]) return home[x] < home[y];
else return a[x][type] > a[y][type];
}
int main() {
scanf("%s", s + 1), read(n);
m = strlen(s + 1);
reverse(s + 1, s + m + 1);
for (int i = 1; i <= m; i++)
if (s[i] == '?') b[i] = -1;
else b[i] = s[i] - '0';
int Max = m;
for (int i = 1; i <= n; i++) {
scanf("\n%s", s + 1);
len[i] = strlen(s + 1);
reverse(s + 1, s + len[i] + 1);
for (int j = 1; j <= len[i]; j++)
a[i][j] = s[j] - '0';
chkmax(len[i], m);
chkmax(Max, len[i]);
}
Max++;
for (int i = 0; i <= 9; i++)
read(val[i]);
for (int i = 0; i <= Max; i++)
for (int j = 0; j <= n; j++)
dp[i][j] = -INF;
dp[0][0] = 0;
static int now[MAXN];
for (int i = 1; i <= n; i++)
now[i] = i;
for (int i = 1; i <= Max; i++) {
int from, to;
if (b[i] == -1) {
if (i == m) from = 1, to = 9;
else from = 0, to = 9;
} else from = to = b[i];
for (int ch = from; ch <= to; ch++) {
int sum = 0, cnt = 0;
for (int j = 1; j <= n; j++)
if (i <= len[j]) {
sum += val[(a[j][i] + ch) % 10];
if (a[j][i] + ch >= 10) cnt++;
}
chkmax(dp[i][cnt], dp[i - 1][0] + sum);
for (int j = 1; j <= n; j++) {
if (i <= len[now[j]]) {
sum -= val[(a[now[j]][i] + ch) % 10];
if (a[now[j]][i] + ch >= 10) cnt--;
}
sum += val[(a[now[j]][i] + ch + 1) % 10];
if (a[now[j]][i] + ch + 1 >= 10) cnt++;
chkmax(dp[i][cnt], dp[i - 1][j] + sum);
}
}
for (int j = 1; j <= n; j++)
home[now[j]] = j;
type = i;
sort(now + 1, now + n + 1, cmp);
}
writeln(dp[Max][0]);
return 0;
}