E - Joint Two Strings
题意
有 N N N 个只包含小写字母的字符串 S 1 . . . S N S_1 ... S_N S1...SN,可以形成 N 2 N^2 N2 个有序对 ( i , j ) (i,j) (i,j),问有多少个字符串 S = S i + S j S = S_i + S_j S=Si+Sj 满足:
- 字符串 T T T 是 S S S 的子序列(非连续的字母)
思路
我们可以维护两个数组 A A A 和 B B B ,其中:
- A i A_i Ai 表示 S i S_i Si 包含的最长 T T T 的前缀字母个数
- B i B_i Bi 表示 S i S_i Si 包含的最长 T T T 的后缀字母个数
那么对于 S = S i + S j S = S_i + S_j S=Si+Sj,只要 A i + B j ≥ ∣ T ∣ ( T 的长度 ) A_i + B_j \geq |T| \quad (T的长度) Ai+Bj≥∣T∣(T的长度) 即可,这样子 T T T 就是 S S S 的子序列了。
所以我们可以枚举第 i i i 个字符串为前缀,那么后缀的 B j B_j Bj 只需要满足 B j ≥ ∣ T ∣ − A i B_j \geq |T| - A_i Bj≥∣T∣−Ai 即可,符合条件的 j j j 的个数就是以 i i i 为前缀的答案。可以直接二层循环来寻找答案,这样子的时间复杂度是 O ( N ) O(N) O(N) 的,因为已经确定了前缀的长度,那么后缀的长度其实已经确定了一个区间范围,最后可以证明遍历的长度不会超过字符串总长度 ∑ i = 1 n ∣ S i ∣ ≤ 5 × 1 0 5 \sum_{i=1}^n |S_i| \leq 5\times10^5 ∑i=1n∣Si∣≤5×105:
时间复杂度为:
∑
i
=
1
n
∑
j
=
∣
T
∣
−
A
i
∣
T
∣
c
n
t
[
j
]
\sum_{i=1}^n \sum_{j=|T| -A_i}^{|T|} cnt[j]
∑i=1n∑j=∣T∣−Ai∣T∣cnt[j],
c
n
t
[
j
]
cnt[j]
cnt[j] 是后缀长度为
j
j
j 的字符数量
=
∑
i
=
1
n
(
∣
T
∣
−
(
∣
T
∣
−
A
i
)
+
1
)
\hspace{50pt} = \sum_{i=1}^{n} (|T| - (|T| - A_i) + 1)
=∑i=1n(∣T∣−(∣T∣−Ai)+1)
=
∑
i
=
1
n
(
A
i
+
1
)
\hspace{50pt} = \sum_{i=1}^{n} (A_i+ 1)
=∑i=1n(Ai+1)
=
∑
i
=
1
n
A
i
\hspace{50pt} = \sum_{i=1}^{n} A_i
=∑i=1nAi
≤
5
×
1
0
5
\hspace{50pt} \leq 5\times 10^5
≤5×105
// Problem: E - Joint Two Strings
// Contest: AtCoder - Japan Registry Services (JPRS) Programming Contest 2023 (AtCoder Beginner Contest 324)
// URL: https://atcoder.jp/contests/abc324/tasks/abc324_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
const int N=500005;
std::string s[N];
int a[N];
int b[N];
ll cnt[N];
int cal(const std::string& s,const std::string& T){
int idx = 0;
for(auto c : s){
if(c == T[idx]) ++idx;
if(idx == (int)T.size()) break;
}
return idx;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int n;
std::string T;
std::cin>>n>>T;
fore(i,1,n+1){
std::cin>>s[i];
a[i] = cal(s[i],T);
}
reverse(T.begin(),T.end());
fore(i,1,n+1){
reverse(s[i].begin(),s[i].end());
b[i] = cal(s[i],T);
}
fore(i,1,n+1) ++cnt[b[i]];
ll ans = 0;
fore(i,1,n+1){ //枚举每一个s[i]来做前缀
int len = (int)T.size() - a[i]; //后缀最起码的长度
fore(j,len,T.size()+1) ans += cnt[j];
}
std::cout<<ans;
return 0;
}
F - Beautiful Path
题意
有一张有向图有
N
N
N 个节点和
M
M
M 条边,每条边有 漂亮值
b
i
b_i
bi 和 花费
c
i
c_i
ci
对于每一条边,都有
u
i
<
v
i
u_i < v_i
ui<vi,即:只有从小节点到大节点的边
问从
1
→
N
1 \rightarrow N
1→N 的路径中
∑
b
i
∑
c
i
\quad \dfrac {\sum b_i}{\sum c_i}
∑ci∑bi 最大 的值是多少?
思路
非常典型的
01
01
01 分数规划,可以设
∑
b
i
∑
c
i
≥
x
\quad \dfrac {\sum b_i}{\sum c_i} \geq x
∑ci∑bi≥x,只要找到最大的
x
x
x 就是答案。移项得:
∑
b
i
−
x
⋅
∑
c
i
≥
0
\sum b_i - x \cdot \sum c_i \geq 0
∑bi−x⋅∑ci≥0
故我们可以把边权改为
b
i
−
x
⋅
c
i
b_i - x \cdot c_i
bi−x⋅ci,只需要跑一条最大路径即可。
由于答案的上界 2 × 1 0 9 2 \times 10^9 2×109 和下界精度 1 0 − 9 10^{-9} 10−9 之间差了大概 2 60 2^{60} 260 倍,因此二分次数设置成 70 70 70 次就差不多了。
如果用 D i j k s t r a Dijkstra Dijkstra 来跑最大路,时间复杂度是 70 ⋅ M ⋅ l o g N ≈ 2 × 1 0 8 70 \cdot M \cdot log \hspace{2pt}N \approx 2 \times 10^8 70⋅M⋅logN≈2×108 会 T I L E TILE TILE
这里有个小 T i p s Tips Tips:
由于这张图的特殊性,只有从小点到大点的边,所以这是一个 D A G DAG DAG,那么跑最大路时,可以依次遍历所有的节点,并更新这个节点对后面的贡献。有点像拓扑排序,当前扫到的节点 i i i 前面的所有点到 i i i 的路径一定都被查看过了。时间复杂度就是: O ( N + M ) O(N+M) O(N+M)
正确的时间复杂度: O ( 70 × ( M + N ) ) O(70 \times (M + N)) O(70×(M+N))
// Problem: F - Beautiful Path
// Contest: AtCoder - Japan Registry Services (JPRS) Programming Contest 2023 (AtCoder Beginner Contest 324)
// URL: https://atcoder.jp/contests/abc324/tasks/abc324_f
// Memory Limit: 1024 MB
// Time Limit: 5000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
const int N=200005;
int n,m;
const double eps = 1e-15;
struct node{
int to;
double b;
double c;
};
std::vector<node> g[N];
bool check(double x){
std::vector<double> d(n+1,-1e20);
d[1] = 0;
fore(u,1,n)
for(auto [v,b,c] : g[u])
d[v] = std::max(d[v],d[u] + b - x*c);
return d[n] >= 0;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
std::cin>>n>>m;
double l = 0 , r = 2e9;
fore(i,0,m){
int u,v;
double b,c;
std::cin>>u>>v>>b>>c;
g[u].push_back({v,b,c});
}
double ans = 0;
fore(i,0,70){
double mid = (l+r)/2.0;
if(check(mid)){
ans = mid;
l = mid;
}
else r = mid;
}
std::cout<<std::fixed<<std::setprecision(15)<<ans;
return 0;
}