URL:https://atcoder.jp/contests/abc324
目录
E
Problem/题意
给出 N 个字符串和 1 个 T 字符串,都由小写字母组成。
现在从 N 个字符串中任取 2 个拼接,如果拼接后的字符串的子序列包含 T 字符串,则这是一种合法拼接。
问有多少种拼接方案。
Thought/思路
每种方案都只能由两个字符串组成,很容易想到:是否能求出有多少个字符串能作为前缀,有多少个字符串能作为后缀。
假设字符串 S[i] 在 T 中的前缀长度为 pre[i],在 T 中的后缀长度为 suf[i]。
那么对于一个二元组(i,j),只要 pre[i] + suf[j] >= len(T),就能作为一种方案。
但是一一匹配肯定是超时的,考虑到遍历过程中我们知道 pre[i] 和 suf[i],就能算出一个值:len(T) - pre[i]。这个值代表着,只要后缀长度大于等于它的,都能作为一次答案。
因此,我们只需要多维护一个 count[i],表示后缀长度为 i 的个数即可,最后的答案就是:
至于这双重循环会不会超时,把 count[j] 当作 1,展开算一下就知道了。
Code/代码
#include "bits/stdc++.h"
#define int long long
int n, pre[500007], suf[500007],count[500007];
std::string T;
int subSequence(std::string s) {
int res = 0, len = s.length();
for (int i = 0, j = 0; i < len; ++ i) {
if (s[i] == T[j]) j ++, res = j;
}
return res;
}
signed main() {
std::cin >> n >> T;
std::vector <std::string> S(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> S[i];
pre[i] = subSequence(S[i]);
}
std::reverse(T.begin(), T.end());
for (int i = 1; i <= n; ++ i) {
std::reverse(S[i].begin(), S[i].end());
suf[i] = subSequence(S[i]);
count[suf[i]] ++;
}
int ans = 0;
for (int i = 1; i <= n; ++ i) {
int r = T.length();
for (int j = r - pre[i]; j <= r; ++ j) {
ans += count[j];
}
}
std::cout << ans;
}
F
Problem/题意
给出一个有向图,每条边有两个值 b 和 c。问从 1 到 N 的路线中,那条路线的 ∑b / ∑c 最大,求出这个最大值。
Thought/思路
分数规划 或 。
在这简单回忆一下分数规划:
因为 在题目环境下通常都有 min、max 值,所以可以二分 X ∈ [min, max]。
一般来说,会以 作为不同题目环境下的权值。(选物品就类似价值,图论就类似边权)
而对于某个 X,我们需要回到题目中明确约束条件,寻找适合 X 的二分条件,选择合适的算法求出:
在这道题中,我们需要求出 的最大值,所以要将 x 最大化,从而得出:
而要满足这个条件,最低要求只需要一条最长路 dis[n] >= 0 即可。(因为在题目中 相当于是边权求和,所以是最长路)
但是求最长路比较麻烦,我们加个负号将其改为最短路:
这时结果判断也要改成 dis[n] <= 0。
Code/代码
#include "bits/stdc++.h"
#define int long long
const int inf = 1e9;
struct node {
int e, b, c;
double d;
};
int n, m;
double dis[200007];
std::vector <node> g[200007];
bool check(double x) {
// 构建边权
for (int i = 1; i <= n; ++ i) {
dis[i] = inf;
for (int j = 0; j < g[i].size(); ++ j) {
g[i][j].d = -(g[i][j].b - x * g[i][j].c);
}
}
// 求最短路
dis[1] = 0;
for (int i = 1; i <= n; ++ i) {
for (int j = 0; j < g[i].size(); ++ j) {
node next = g[i][j];
dis[next.e] = std::min(dis[next.e], next.d + dis[i]);
}
}
return dis[n] <= 0; // 加了负号,这里也要改
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
std::cin >> n >> m;
for (int i = 1; i <= m; ++ i) {
int x, y, b, c;
std::cin >> x >> y >> b >> c;
g[x].push_back({y, b, c, 0.0});
}
double l = 0, r = 10000;
while (r - l > 1e-10) {
double mid = (r + l) / 2;
if (check(mid)) l = mid;
else r = mid;
}
std::cout << std::fixed << std::setprecision(10) << l;
}