归档 221018 - 221021 | 做题记录
很难评价我的做题记录、勇哥的笔记和 coolcool 村村长的视频哪个更意识流。
K. Difference - 221018
好耶我会打 \(n^3\)!这说明这道题肯定是一个我不会写的 \(n^2\)!
\(n\) 是 \(10^6\)????寄……
枚举出现次数最多的字符(假设为 \(x\))和出现次数最少的字符(假设为 \(y\))。
然后用一个类似于双指针的东西,额,我解释不大来,总之意思就是我们要让 \(y\) 和 \(x\) 第一次出现的地方分别成为起始区间的起点和终点。
说是起点和终点,但实际上你不用特别在意它们一开始的前后顺序,因为我们会把 \(26\times 26\) 中情况全部枚举一遍。。。而且不排除存在 \(x\) 反杀的可能性。
然后,如果右端点能往右挪,多塞下一个 \(x\),我就肯定往右挪,此时统计的答案增加 \(1\)。如果 \(x\) 挪不了了,那就往右挪 \(y\),不停下来是因为虽然 \(x\) 现在一时吃瘪,但后面还是有可能反杀 \(y\)。
那么问题来了,你怎么知道你枚举的这个区间里 \(x\) 一定出现次数最多,\(y\) 一定出现次数最少???
我可没这么说过啊,思考,如果 \(x\) 出现次数不是最多,\(y\) 出现次数不是最少,说明答案也不是这个区间里最多的,所以后面还会被更新。
关于「往右挪」这个操作,我们预处理一下某个字符某一次出现的位置就好。
然后注意跳的时候如果出现了数量统计为负的情况就可以从 0 开始统计了!
然后最后的时间复杂度是 \(\mathcal O(n)\),因为要非常严谨地 KA 掉常数,虽然这个常数是一个明显跑不满的 \(10^2\) 级别。。。
namespace XSC062 {
using namespace fastIO;
const int maxm = 28;
const int maxn = 1e6 + 5;
char s[maxn];
int cnt[maxm];
int d[maxm][maxn];
int n, l, r, res, ans, t, f;
inline int max(int x, int y) {
return x > y ? x : y;
}
int main() {
read(n);
reads(s + 1);
for (int i = 1; i <= n; ++i)
d[s[i] - 'a' + 1][++cnt[s[i] - 'a' + 1]] = i;
for (int x = 1; x <= 26; ++x) {
if (!cnt[x])
continue;
for (int y = 1; y <= 26; ++y) {
if (!cnt[y] || x == y)
continue;
f = 0;
t = cnt[x] + cnt[y];
l = 1, r = 1, res = -1;
while (t--) {
if ((l <= cnt[x] && d[x][l] < d[y][r])
|| r > cnt[y])
++l, ++res;
else ++r, res -= f, f = 1;
if (res < 0)
f = 0;
ans = max(ans, res);
}
}
}
print(ans);
return 0;
}
} // namespace XSC062
L. 前缀单词 - 221019
https://www.luogu.com.cn/problem/P1666
智力过来问我:有什么办法可以在一个图里找到一些边,满足它们没有公共端点?
我:……
我:……一般图最大匹配?
后来反应过来智力问的是这道题。不过他倒是给了我一个思路,可以把它转换成连边的形式来做。
先摆一会儿。
C. 金字塔 - 221021
这道题是不是很简单啊,怎么感觉除了我所有人都会做。。。
https://www.acwing.com/problem/content/286/
就是给了一个欧拉序,求可能的树的形状数量。
那它为什么是区间 DP 呢?我不知道…… 如果它不放在区间 DP 的链接里面我肯定是看不出来的……
因为从来就没有弄懂过这道题,今天决定完全自己想一次。
首先,因为是区间 DP,所以 f[i][j]
表示的应该是 \(s_i\sim s_j\) 组成树的方案数。
此时 \(s_i\) 一定等于 \(s_j\)。
那么怎么转移呢?不妨先从简单的情况看起。
若 \(s_i\sim s_j\) 中,根节点只包含一个子树,那么方案数就是 f[i + 1][j - 1]
。
如果包含两个,在中间寻找一个点 \(k\) 使得 \(s_i=s_k\),方案数为 f[i + 1][k - 1] * f[k + 1][j]
。
如果包含三个,在中间寻找点 \(k_1\)、\(k_2\),使得 \(k_1 < k_2\) 且 \(s_i=s_{k_1}=s_{k_2}\),方案数为 f[i + 1][k1 - 1] * f[k1 + 1][k2 - 1] * f[k2 + 1][j - 1]
。
我们发现到这个时候式子就已经很复杂了。但注意到 \(s_i=s_{k_1}=s_{k_2}\),而 f[i][j]
满足 \(s_i = s_j\),不难想到将 f[i + 1][k1 - 1]
转化为 f[i][k1]
,将其视为树。
但如果直接转移会有重复情况。
这个时候我们选择,f[i][j] = f[i][k] * f[k + 1][j - 1]
,将右边那一坨视为独立的、不以 \(s_i\) 为根的树。
namespace XSC062 {
using namespace fastIO;
const int mod = 1e9;
const int maxn = 305;
int n;
char a[maxn];
int f[maxn][maxn];
int main() {
reads(a + 1);
n = strlen(a + 1);
for (int i = 1; i <= n; ++i)
f[i][i] = 1;
for (int l = 2; l <= n; ++l) {
for (int i = 1; i <= n - l + 1; ++i) {
int j = i + l - 1;
if (a[i] != a[j])
continue;
for (int k = i; k < j - 1; ++k) {
if (a[k] != a[i])
continue;
(f[i][j] += f[i][k]
* f[k + 1][j - 1] % mod) %= mod;
}
}
}
print(f[1][n]);
return 0;
}
} // namespace XSC062
D. 2-05F. zzj & liaoy の 毁灭打击
不难发现(假的,其实想了很久),如果我们要更新 \(i\sim j\) 的答案,会受到相邻区间的影响。不妨设 f[i][j]
为把 \(i\sim j\) 内全部删除,只剩 \(i\) 和 \(j\) 的最大收益,因为 \(i\) 和 \(j\) 必须保留,有 f[i][j] = max {f[i][k] + f[k][j] + a[i] * a[j]}
。
namespace XSC062 {
using namespace fastIO;
const int maxn = 303;
int n;
int a[maxn];
int f[maxn][maxn], p[maxn][2];
inline int max(int x, int y) {
return x > y ? x : y;
}
int main() {
read(n);
a[1] = a[n + 2] = 1;
for (int i = 2; i <= n + 1; ++i)
read(a[i]);
n += 2;
for (int l = 3; l <= n; ++l) {
for (int i = 1; i <= n - l + 1; ++i) {
int j = i + l - 1;
for (int k = i + 1; k < j; ++k)
f[i][j] = max(f[i][j], f[i][k]
+ f[k][j] + a[i] * a[j]);
}
}
print(f[1][n]);
return 0;
}
} // namespace XSC062
E. 基因串 - 221021
题目叫我优化掉 \(26^2\) 的系数,但我就不 😤(理直气壮)
可是很明显唯一 KA 掉了 \(26^2\) 的代码(link)是神啊,我看不懂 🙃
注意到询问的长度上限是 \(100\)。对于每一次询问,我们定义 f[i][j][k]
表示 \(i\sim j\) 是否能缩成字母 \(k\)。
然后就很显而易见了,跑一个区间 DP,然后再来另一个区间 DP,计算 \(i\sim j\) 会缩成多少个 S。
namespace XSC062 {
using namespace fastIO;
const int maxm = 35;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int x, y, z;
int s[maxn];
char a, b, c;
char t[maxn];
int n, m, len;
int dp[maxn][maxn];
int cnt[maxm][maxm];
bool f[maxn][maxn][maxm];
int rul[maxm][maxm][maxn];
inline int min(int x, int y) {
return x < y ? x : y;
}
int main() {
read(n);
for (int i = 1; i <= n; ++i) {
readc(c), readc(a), readc(b);
x = a - 'A' + 1;
y = b - 'A' + 1;
z = c - 'A' + 1;
rul[x][y][++cnt[x][y]] = z;
}
read(m);
while (m--) {
reads(t + 1);
len = strlen(t + 1);
memset(f, 0, sizeof (f));
for (int i = 1; i <= len; ++i) {
s[i] = t[i] - 'A' + 1;
f[i][i][s[i]] = 1;
if (i == 1)
continue;
for (int j = 1; j <= cnt[s[i - 1]][s[i]]; ++j)
f[i - 1][i][rul[s[i - 1]][s[i]][j]] = 1;
}
for (int l = 3; l <= len; ++l) {
for (int i = 1; i <= len - l + 1; ++i) {
int j = i + l - 1;
for (int k = i; k < j; ++k) {
for (int k1 = 1; k1 <= 26; ++k1) {
if (!f[i][k][k1])
continue;
for (int k2 = 1; k2 <= 26; ++k2) {
if (!f[k + 1][j][k2])
continue;
for (int k3 = 1; k3 <= cnt[k1][k2];
++k3)
f[i][j][rul[k1][k2][k3]] = 1;
}
}
}
}
}
for (int i = 1; i <= len; ++i) {
for (int j = i; j <= len; ++j)
dp[i][j] = f[i][j]['S' - 'A' + 1] ? 1 : inf;
}
for (int l = 3; l <= len; ++l) {
for (int i = 1; i <= len - l + 1; ++i) {
int j = i + l - 1;
for (int k = i; k < j; ++k) {
dp[i][j] = min(dp[i][j], dp[i][k]
+ dp[k + 1][j]);
}
}
}
if (dp[1][len] == inf)
puts("NIE");
else print(dp[1][len], '\n');
}
return 0;
}
} // namespace XSC062
F. 马棚问题 - 221021
首先我们跑一个区间 DP,算出把 \(i\sim j\) 放到一个马棚的代价。
然后再跑一个线性 DP,得到答案。
namespace XSC062 {
using namespace fastIO;
const int maxn = 505;
const int inf = 0x3f3f3f3f;
int n, k;
int a[maxn];
int f[maxn][maxn], dp[maxn][maxn];
inline int min(int x, int y) {
return x < y ? x : y;
}
int main() {
#ifdef ONLINE_JUDGE
freopen("horse.in", "r", stdin);
freopen("horse.out", "w", stdout);
#endif
read(n), read(k);
for (int i = 1; i <= n; ++i)
read(a[i]);
for (int l = 1; l <= n; ++l) {
for (int i = 1; i <= n - l + 1; ++i) {
int j = i + l - 1, t0 = 0, t1 = 0;
for (int k = i; k <= j; ++k) {
if (a[k] == 0)
++t0;
else ++t1;
}
f[i][j] = t0 * t1;
}
}
memset(dp, 0x3f, sizeof (dp));
dp[0][0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= k; ++j) {
for (int k = 0; k < i; ++k) {
dp[i][j] = min(dp[i][j], dp[k][j - 1]
+ f[k + 1][i]);
}
}
}
print(dp[n][k]);
return 0;
}
} // namespace XSC062
—— · EOF · ——
真的什么也不剩啦 😖