20241222 总结
20241222 总结
A - CodeForces - 1350B
简化题意:给定一个长度为 \(n\) 的序列 \(A\),求一个 \(A\) 的长度为 \(m\) 的严格上升子序列 \(B\),设其在 \(A\) 中对应位置为 \(p\) 序列,使得对于任意 \(1\le i < m\) 均有 \(p_i | p_{i+1}\),求满足条件的最大的 \(m\)。
思路:首先定义状态 \(f_i\) 表示以 \(i\) 结尾的符合条件的子序列长度,则朴素的转移是枚举两个位置 \(1\le j < i\le n\),每次判断 \(j | i\) 是否成立,成立则 \(f_i\gets\max (f_i,f_j+1)\),这种算法时间复杂度为 \(\Omicron (n^2)\),在 \(1\le n\le 10^5\) 范围内无法通过。考虑优化,我们可以直接枚举 \(i\) 的倍数进行转移,利用数学知识可知,该算法复杂度降为 \(\Omicron (n\ln n)\),可以通过。
细节与难点:此题难点在于正确分析算法时间复杂度,实现未遇到困难。
B - AtCoder - abc275_f
简化题意:给定一个长度为 \(n\) 的序列 \(A\),每次操作可以从其中标记一个连续子段,求使得未标记部分之和为 \(1\le x\le m\) 的最小操作次数。
思路:首先观察到 \(1\le n\le 3\times 10^3\),适合 \(\Omicron (n^2\log n)\) 复杂度以下算法。我们可以定义状态 \(f_{i,j,0/1}\) 表示当前到第 \(i\) 位,前 \(i\) 项中未被标记的数之和为 \(j\),当前位标记(0)/不标记(1)的最少操作次数。则我们定义转移方程:
表示第 \(i\) 位标记时,由上一位标记这一位标记、上一位不标记这一位标记转移而来;
表示第 \(i\) 位不标记时,由上一位不标记这一位不标记、上一位标记这一位不标记转移而来。这种转移方程使算法时间复杂度为 \(\Omicron (n^2)\),可以通过。边界条件为 \(f_{1,a_1,1}=0,f_{1,0,0}=1\)。
细节与难点:此题难点在于转移时的细节,我在这方面出现的问题是 \(f_{i,j,0}\) 转移时因为 \(j\) 没有变化,可以枚举 \(0\sim m\) 中的所有情况,而我只枚举了 \(a_i\sim m\) 中的情况,且初步修改时以为是循环顺序不正确,经老师点拨改正。
C - AtCoder - abc265_e
简化题意:从原点出发,每次可以从 \((x,y)\) 移动至 \((x+a,y+b)\)、\((x+c,y+d)\)、\((x+e,y+f)\),且不可到达 \((p_1,q_1)\)、\((p_2,q_2)\)、\(\cdots\)、\((p_m,q_m)\)。求移动 \(n\) 次后有多少种不同移动路径。
思路:注意到 \(1\le n\le 3\times 10^2\),且只有三种移动方式,定义状态 \(f_{i,r_1,r_2,r_3}\) 表示共移动 \(i\) 次,其中三种移动方式分别的次数分别为 \(r_1,r_2,r_3\)。枚举上述四变量,则当前点坐标 \((x,y)=(r_1*a+r_2*c+r_3*e,r_1*b+r_2*d+r_3*f)\) ,三个移动后的点如题意,则用 map 判断新点是否可以到达,并扩散转移:
统计答案即为 \(i=n\) 时 \(f_{i,r_1,r_2,r_3}\) 之和。
注意到上述转移复杂度为 \(\Omicron (n^4)\),仍无法通过,注意到 \(r_3=i-r_1-r_2\),可省去一维复杂度,降为 \(\Omicron (n^3)\)。
细节与难点:转移基本无难度,但是注意细节:map 判断点是否能到达时不可用中括号访问,这样相当于在 map 中新添加了一对映射,复杂度变劣,应使用 count 或 find 函数,多重循环循环变量需辨认清楚。
D - AtCoder - abc244_e
简化题意:给定一个 \(n\) 个点 \(m\) 条边的无向图 \(G\),求其中有多少条首尾分别为 \(S\) 和 \(T\) 的长度为 \(k\) 的路径(不一定为简单路径),满足 \(X\) 点被经过偶数次。
思路:定义状态 \(f_{i,j,0/1}\) 表示经过 \(i\) 条边后到达 \(j\) 点,总共经过偶数(0)/奇数(1)次 \(X\) 点。则我们枚举 \(i\) 、边的起点 \(u\)、边的终点 \(v\),则转移为:
若 \(v\ne x\) 则:
表示经过 \(X\) 的次数不变,奇偶性不变;
若 \(v=x\) 则:
表示经过 \(X\) 的次数加一,奇偶性变化。
答案即为 \(f_{k,T,0}\)。上述看似枚举了三个变量,实则枚举 \(u,v\) 只有 \(\Omicron (m)\),时间复杂度为 \(\Omicron (km)\)。
细节与难点:我使用了一个辅助数组 \(g\) 用来滚掉 \(f\) 数组中 \(i\) 的一维,但是每次没有清空原数组 \(f\) 以达到滚动数组的效果,这样做时需要注意,或者在如本题一样没有卡死空间的题目不滚动,开满维度。
F - CodeForces - 1077F1
简化题意:给定一个长度为 \(n\) 的序列 \(A\),要求从中选出恰好 \(x\) 项,使得 \(A\) 的每个长为 \(k\) 的连续子段都有一项被选中,求选中的项之和的最大值或报告无解。
思路:注意到本题 \(1\le k,x\le n\le 2\times 10^2\) 的较小数据范围,考虑 \(\Omicron (n^3)\) 朴素 DP。我们可以设计状态 \(f_{l,i}\) 表示 \(A_1\sim A_i\) 中选的第 \(l\) 个为 \(A_i\) 的最大和。则我们枚举 \(l,i\)(\(1\le l\le x,i\le n\)),再枚举选中的第 \(l-1\) 个数的位置 \(j\)(\(\max (0,i-k)\le j < i\)),则有转移方程 \(f_{l,i}\gets\max (f_{l,i},f_{l-1,j}+A_i)\)。则答案即为 \(\max\limits_{i=n-k+1}^{n} f_{x,i}\)。
细节与难点:我在初始化出了问题,一开始我没有给 DP 数组 \(f\) 赋初值 \(-\inf\),导致在不满足题意要求的情况下也进行了转移与答案统计。
G - CodeForces - 1077F2
简化题意:同上。
思路:本题数据范围扩大至 \(5\times 10^3\) 级别,注意到上述转移中有 \(f_{l,i}\gets\max (f_{l,i},f_{l-1,j}+A_i)\) 一式,发现其中 \(f_{l-1,j}\) 可以用单调队列动态维护最大值,沿用原式转移即可,时间复杂度 \(\Omicron (n^2)\)。
细节与难点:无。注意单调队列模板中的 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 ();
}

浙公网安备 33010602011771号