20241222 总结
A - CodeForces - 1350B
简化题意:给定一个长度为 n n n 的序列 A A A,求一个 A A A 的长度为 m m m 的严格上升子序列 B B B,设其在 A A A 中对应位置为 p p p 序列,使得对于任意 1 ≤ i < m 1\le i < m 1≤i<m 均有 p i ∣ p i + 1 p_i | p_{i+1} pi∣pi+1,求满足条件的最大的 m m m。
思路:首先定义状态 f i f_i fi 表示以 i i i 结尾的符合条件的子序列长度,则朴素的转移是枚举两个位置 1 ≤ j < i ≤ n 1\le j < i\le n 1≤j<i≤n,每次判断 j ∣ i j | i j∣i 是否成立,成立则 f i ← max ( f i , f j + 1 ) f_i\gets\max (f_i,f_j+1) fi←max(fi,fj+1),这种算法时间复杂度为 O ( n 2 ) \Omicron (n^2) O(n2),在 1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1≤n≤105 范围内无法通过。考虑优化,我们可以直接枚举 i i i 的倍数进行转移,利用数学知识可知,该算法复杂度降为 O ( n ln n ) \Omicron (n\ln n) O(nlnn),可以通过。
细节与难点:此题难点在于正确分析算法时间复杂度,实现未遇到困难。
B - AtCoder - abc275_f
简化题意:给定一个长度为 n n n 的序列 A A A,每次操作可以从其中标记一个连续子段,求使得未标记部分之和为 1 ≤ x ≤ m 1\le x\le m 1≤x≤m 的最小操作次数。
思路:首先观察到 1 ≤ n ≤ 3 × 1 0 3 1\le n\le 3\times 10^3 1≤n≤3×103,适合 O ( n 2 log n ) \Omicron (n^2\log n) O(n2logn) 复杂度以下算法。我们可以定义状态 f i , j , 0 / 1 f_{i,j,0/1} fi,j,0/1 表示当前到第 i i i 位,前 i i i 项中未被标记的数之和为 j j j,当前位标记(0)/不标记(1)的最少操作次数。则我们定义转移方程:
f i , j , 0 ← min { f i , j , 0 , f i − 1 , j , 0 , f i − 1 , j , 1 + 1 } f_{i,j,0}\gets\min\{f_{i,j,0},f_{i-1,j,0},f_{i-1,j,1}+1\} fi,j,0←min{fi,j,0,fi−1,j,0,fi−1,j,1+1}
表示第 i i i 位标记时,由上一位标记这一位标记、上一位不标记这一位标记转移而来;
f i , j , 1 ← min { f i , j , 1 , f i − 1 , j − a i , 0 , f i − 1 , j − a i , 1 } f_{i,j,1}\gets\min\{f_{i,j,1},f_{i-1,j-a_i,0},f_{i-1,j-a_i,1}\} fi,j,1←min{fi,j,1,fi−1,j−ai,0,fi−1,j−ai,1}
表示第 i i i 位不标记时,由上一位不标记这一位不标记、上一位标记这一位不标记转移而来。这种转移方程使算法时间复杂度为 O ( n 2 ) \Omicron (n^2) O(n2),可以通过。边界条件为 f 1 , a 1 , 1 = 0 , f 1 , 0 , 0 = 1 f_{1,a_1,1}=0,f_{1,0,0}=1 f1,a1,1=0,f1,0,0=1。
细节与难点:此题难点在于转移时的细节,我在这方面出现的问题是 f i , j , 0 f_{i,j,0} fi,j,0 转移时因为 j j j 没有变化,可以枚举 0 ∼ m 0\sim m 0∼m 中的所有情况,而我只枚举了 a i ∼ m a_i\sim m ai∼m 中的情况,且初步修改时以为是循环顺序不正确,经老师点拨改正。
C - AtCoder - abc265_e
简化题意:从原点出发,每次可以从 ( x , y ) (x,y) (x,y) 移动至 ( x + a , y + b ) (x+a,y+b) (x+a,y+b)、 ( x + c , y + d ) (x+c,y+d) (x+c,y+d)、 ( x + e , y + f ) (x+e,y+f) (x+e,y+f),且不可到达 ( p 1 , q 1 ) (p_1,q_1) (p1,q1)、 ( p 2 , q 2 ) (p_2,q_2) (p2,q2)、 ⋯ \cdots ⋯、 ( p m , q m ) (p_m,q_m) (pm,qm)。求移动 n n n 次后有多少种不同移动路径。
思路:注意到 1 ≤ n ≤ 3 × 1 0 2 1\le n\le 3\times 10^2 1≤n≤3×102,且只有三种移动方式,定义状态 f i , r 1 , r 2 , r 3 f_{i,r_1,r_2,r_3} fi,r1,r2,r3 表示共移动 i i i 次,其中三种移动方式分别的次数分别为 r 1 , r 2 , r 3 r_1,r_2,r_3 r1,r2,r3。枚举上述四变量,则当前点坐标 ( x , y ) = ( r 1 ∗ a + r 2 ∗ c + r 3 ∗ e , r 1 ∗ b + r 2 ∗ d + r 3 ∗ f ) (x,y)=(r_1*a+r_2*c+r_3*e,r_1*b+r_2*d+r_3*f) (x,y)=(r1∗a+r2∗c+r3∗e,r1∗b+r2∗d+r3∗f) ,三个移动后的点如题意,则用 map 判断新点是否可以到达,并扩散转移:
f i + 1 , r 1 + 1 , r 2 , r 3 ← f i + 1 , r 1 + 1 , r 2 , r 3 + f i , r 1 , r 2 , r 3 f_{i+1,r_1+1,r_2,r_3}\gets f_{i+1,r_1+1,r_2,r_3}+f_{i,r_1,r_2,r_3} fi+1,r1+1,r2,r3←fi+1,r1+1,r2,r3+fi,r1,r2,r3
f i + 1 , r 1 , r 2 + 1 , r 3 ← f i + 1 , r 1 , r 2 + 1 , r 3 + f i , r 1 , r 2 , r 3 f_{i+1,r_1,r_2+1,r_3}\gets f_{i+1,r_1,r_2+1,r_3}+f_{i,r_1,r_2,r_3} fi+1,r1,r2+1,r3←fi+1,r1,r2+1,r3+fi,r1,r2,r3
f i + 1 , r 1 , r 2 , r 3 + 1 ← f i + 1 , r 1 , r 2 , r 3 + 1 + f i , r 1 , r 2 , r 3 f_{i+1,r_1,r_2,r_3+1}\gets f_{i+1,r_1,r_2,r_3+1}+f_{i,r_1,r_2,r_3} fi+1,r1,r2,r3+1←fi+1,r1,r2,r3+1+fi,r1,r2,r3
统计答案即为 i = n i=n i=n 时 f i , r 1 , r 2 , r 3 f_{i,r_1,r_2,r_3} fi,r1,r2,r3 之和。
注意到上述转移复杂度为 O ( n 4 ) \Omicron (n^4) O(n4),仍无法通过,注意到 r 3 = i − r 1 − r 2 r_3=i-r_1-r_2 r3=i−r1−r2,可省去一维复杂度,降为 O ( n 3 ) \Omicron (n^3) O(n3)。
细节与难点:转移基本无难度,但是注意细节:map 判断点是否能到达时不可用中括号访问,这样相当于在 map 中新添加了一对映射,复杂度变劣,应使用 count 或 find 函数,多重循环循环变量需辨认清楚。
D - AtCoder - abc244_e
简化题意:给定一个 n n n 个点 m m m 条边的无向图 G G G,求其中有多少条首尾分别为 S S S 和 T T T 的长度为 k k k 的路径(不一定为简单路径),满足 X X X 点被经过偶数次。
思路:定义状态 f i , j , 0 / 1 f_{i,j,0/1} fi,j,0/1 表示经过 i i i 条边后到达 j j j 点,总共经过偶数(0)/奇数(1)次 X X X 点。则我们枚举 i i i 、边的起点 u u u、边的终点 v v v,则转移为:
若 v ≠ x v\ne x v=x 则:
f i , v , 0 ← f i , v , 0 + f i − 1 , u , 0 f_{i,v,0}\gets f_{i,v,0}+f_{i-1,u,0} fi,v,0←fi,v,0+fi−1,u,0
f i , v , 1 ← f i , v , 1 + f i − 1 , u , 1 f_{i,v,1}\gets f_{i,v,1}+f_{i-1,u,1} fi,v,1←fi,v,1+fi−1,u,1
表示经过 X X X 的次数不变,奇偶性不变;
若 v = x v=x v=x 则:
f i , v , 0 ← f i , v , 0 + f i − 1 , u , 1 f_{i,v,0}\gets f_{i,v,0}+f_{i-1,u,1} fi,v,0←fi,v,0+fi−1,u,1
f i , v , 1 ← f i , v , 1 + f i − 1 , u , 0 f_{i,v,1}\gets f_{i,v,1}+f_{i-1,u,0} fi,v,1←fi,v,1+fi−1,u,0
表示经过 X X X 的次数加一,奇偶性变化。
答案即为 f k , T , 0 f_{k,T,0} fk,T,0。上述看似枚举了三个变量,实则枚举 u , v u,v u,v 只有 O ( m ) \Omicron (m) O(m),时间复杂度为 O ( k m ) \Omicron (km) O(km)。
细节与难点:我使用了一个辅助数组 g g g 用来滚掉 f f f 数组中 i i i 的一维,但是每次没有清空原数组 f f f 以达到滚动数组的效果,这样做时需要注意,或者在如本题一样没有卡死空间的题目不滚动,开满维度。
F - CodeForces - 1077F1
简化题意:给定一个长度为 n n n 的序列 A A A,要求从中选出恰好 x x x 项,使得 A A A 的每个长为 k k k 的连续子段都有一项被选中,求选中的项之和的最大值或报告无解。
思路:注意到本题 1 ≤ k , x ≤ n ≤ 2 × 1 0 2 1\le k,x\le n\le 2\times 10^2 1≤k,x≤n≤2×102 的较小数据范围,考虑 O ( n 3 ) \Omicron (n^3) O(n3) 朴素 DP。我们可以设计状态 f l , i f_{l,i} fl,i 表示 A 1 ∼ A i A_1\sim A_i A1∼Ai 中选的第 l l l 个为 A i A_i Ai 的最大和。则我们枚举 l , i l,i l,i( 1 ≤ l ≤ x , i ≤ n 1\le l\le x,i\le n 1≤l≤x,i≤n),再枚举选中的第 l − 1 l-1 l−1 个数的位置 j j j( max ( 0 , i − k ) ≤ j < i \max (0,i-k)\le j < i max(0,i−k)≤j<i),则有转移方程 f l , i ← max ( f l , i , f l − 1 , j + A i ) f_{l,i}\gets\max (f_{l,i},f_{l-1,j}+A_i) fl,i←max(fl,i,fl−1,j+Ai)。则答案即为 max i = n − k + 1 n f x , i \max\limits_{i=n-k+1}^{n} f_{x,i} i=n−k+1maxnfx,i。
细节与难点:我在初始化出了问题,一开始我没有给 DP 数组 f f f 赋初值 − inf -\inf −inf,导致在不满足题意要求的情况下也进行了转移与答案统计。
G - CodeForces - 1077F2
简化题意:同上。
思路:本题数据范围扩大至 5 × 1 0 3 5\times 10^3 5×103 级别,注意到上述转移中有 f l , i ← max ( f l , i , f l − 1 , j + A i ) f_{l,i}\gets\max (f_{l,i},f_{l-1,j}+A_i) fl,i←max(fl,i,fl−1,j+Ai) 一式,发现其中 f l − 1 , j f_{l-1,j} fl−1,j 可以用单调队列动态维护最大值,沿用原式转移即可,时间复杂度 O ( n 2 ) \Omicron (n^2) O(n2)。
细节与难点:无。注意单调队列模板中的 front 和 back 不要写反。
My Code
A
#include <bits/stdc++.h>
using namespace std;
namespace rab {
int t=-1,n,a[100010],f[100010];
int main () {
if (!~t) cin>> t;
if (!t--) return 0;
cin>> n;f[0]=0;
fill (f+1,f+n+1,1);
for (int i=1;i<=n;i++) cin>> a[i];
for (int i=1;i<=n;i++)
for (int j=2;i*j<=n;j++)
if (a[i]<a[i*j]) f[i*j]=max (f[i*j],f[i]+1);
for (int i=1;i<=n;i++) f[0]=max (f[0],f[i]);
cout<< f[0]<< "\n";
return main ();
}
}
int main () {
ios::sync_with_stdio (0);
cin.tie (0);
cout.tie (0);
return rab::main ();
}
B
#include <bits/stdc++.h>
using namespace std;
namespace rab {
const int inf=3000;
int n,m,a[3010],f[3010][3010][2];
int main () {
cin>> n>> m;
for (int i=1;i<=n;i++) {
cin>> a[i];
for (int j=0;j<=m;j++)
f[i][j][0]=f[i][j][1]=inf;
}
f[1][a[1]][1]=0;
f[1][0][0]=1;
for (int i=2;i<=n;i++) {
for (int j=m;~j;j--) {
f[i][j][0]=min ({f[i][j][0],f[i-1][j][0],f[i-1][j][1]+1});
if (j>=a[i]) f[i][j][1]=min ({f[i][j][1],f[i-1][j-a[i]][0],f[i-1][j-a[i]][1]});
}
}
for (int i=1;i<=m;i++) {
int ans=min (f[n][i][0],f[n][i][1]);
if (ans^inf) cout<< ans<< "\n";
else cout<< "-1\n";
}
return 0;
}
}
int main () {
ios::sync_with_stdio (0);
cin.tie (0);
cout.tie (0);
return rab::main ();
}
C
#include <bits/stdc++.h>
using namespace std;
#define i64 long long
#define pii pair<i64,i64>
namespace rab {
const int mod=998244353;
int n,m;
i64 a,b,c,d,e,f,ans,dp[310][310][310];
map<pii,bool> mp;
int main () {
cin>> n>> m>> a>> b>> c>> d>> e>> f;
for (int i=1,x,y;i<=m;i++) {
cin>> x>> y;
mp[{x,y}]=1;
}
dp[0][0][0]=1;
for (int i=0;i<n;i++) {
for (int r1=0;r1<=i;r1++) {
for (int r2=0;r1+r2<=i;r2++) {
int r3=i-r1-r2;
i64 x=r1*a+r2*c+r3*e;
i64 y=r1*b+r2*d+r3*f;
if (!mp.count ({x+a,y+b})) (dp[i+1][r1+1][r2]+=dp[i][r1][r2])%=mod;
if (!mp.count ({x+c,y+d})) (dp[i+1][r1][r2+1]+=dp[i][r1][r2])%=mod;
if (!mp.count ({x+e,y+f})) (dp[i+1][r1][r2]+=dp[i][r1][r2])%=mod;
}
}
}
for (int i=0;i<=n;i++)
for (int j=0;i+j<=n;j++)
(ans+=dp[n][i][j])%=mod;
cout<< ans;
return 0;
}
}
#undef i64
#undef pii
int main () {
ios::sync_with_stdio (0);
cin.tie (0);
cout.tie (0);
return rab::main ();
}
D
#include <bits/stdc++.h>
using namespace std;
#define i64 long long
namespace rab {
const int mod=998244353;
int n,m,k,s,t,x;
i64 f[2010][2],g[2010][2];
vector<int> e[2010];
int main () {
cin>> n>> m>> k>> s>> t>> x;
for (int i=1,u,v;i<=m;i++) {
cin>> u>> v;
e[u].emplace_back (v);
e[v].emplace_back (u);
}
for (int v: e[s]) {
if (v^x) f[v][0]=1;
else f[v][1]=1;
}
for (int i=2;i<=k;i++) {
for (int u=1;u<=n;u++) {
g[u][0]=f[u][0];
g[u][1]=f[u][1];
f[u][0]=f[u][1]=0;
}
for (int u=1;u<=n;u++) {
for (int v: e[u]) {
if (v^x) {
(f[v][0]+=g[u][0])%=mod;
(f[v][1]+=g[u][1])%=mod;
} else {
(f[v][0]+=g[u][1])%=mod;
(f[v][1]+=g[u][0])%=mod;
}
}
}
}
cout<< f[t][0];
return 0;
}
}
#undef i64
int main () {
ios::sync_with_stdio (0);
cin.tie (0);
cout.tie (0);
return rab::main ();
}
F
#include <bits/stdc++.h>
using namespace std;
#define i64 long long
namespace rab {
const i64 inf=2e12;
int n,k,x,a[510];
i64 ans=-inf,f[510][510];
deque<int> q;
int main () {
cin>> n>> k>> x;
for (int i=1;i<=n;i++) cin>> a[i];
if (x<n/k) {
cout<< -1;
return 0;
}
for (int i=0;i<=x;i++)
for (int j=0;j<=n;j++)
f[i][j]=-inf;
f[0][0]=0;
for (int l=1;l<=x;l++) {
for (int i=l;i<=n;i++) {
for (int j=max ({0,i-k});j<i;j++) {
f[l][i]=max (f[l][i],f[l-1][j]+a[i]);
}
}
}
for (int i=n-k+1;i<=n;i++)
ans=max (ans,f[x][i]);
cout<< ans;
return 0;
}
}
#undef i64
int main () {
ios::sync_with_stdio (0);
cin.tie (0);
cout.tie (0);
return rab::main ();
}
G
#include <bits/stdc++.h>
using namespace std;
#define i64 long long
namespace rab {
const i64 inf=5e13;
int n,k,x,a[5010];
i64 ans=-inf,f[5010][5010];
deque<int> q;
int main () {
cin>> n>> k>> x;
for (int i=1;i<=n;i++) cin>> a[i];
if (x<n/k) {
cout<< -1;
return 0;
}
for (int i=0;i<=x;i++)
for (int j=0;j<=n;j++)
f[i][j]=-inf;
f[0][0]=0;
for (int l=1;l<=x;l++) {
q.clear ();
q.push_back (0);
for (int i=1;i<=n;i++) {
while (q.size ()&&f[l-1][q.back ()]<=f[l-1][i-1]) q.pop_back ();
while (q.size ()&&q.front ()<max (0,i-k)) q.pop_front ();
q.push_back (i-1);
f[l][i]=f[l-1][q.front ()]+a[i];
}
}
for (int i=n-k+1;i<=n;i++)
ans=max (ans,f[x][i]);
cout<< ans;
return 0;
}
}
#undef i64
int main () {
ios::sync_with_stdio (0);
cin.tie (0);
cout.tie (0);
return rab::main ();
}