20241222 总结

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 1i<m 均有 p i ∣ p i + 1 p_i | p_{i+1} pipi+1,求满足条件的最大的 m m m

思路:首先定义状态 f i f_i fi 表示以 i i i 结尾的符合条件的子序列长度,则朴素的转移是枚举两个位置 1 ≤ j < i ≤ n 1\le j < i\le n 1j<in,每次判断 j ∣ i j | i ji 是否成立,成立则 f i ← max ⁡ ( f i , f j + 1 ) f_i\gets\max (f_i,f_j+1) fimax(fi,fj+1),这种算法时间复杂度为 O ( n 2 ) \Omicron (n^2) O(n2),在 1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1n105 范围内无法通过。考虑优化,我们可以直接枚举 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 1xm 的最小操作次数。

思路:首先观察到 1 ≤ n ≤ 3 × 1 0 3 1\le n\le 3\times 10^3 1n3×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,0min{fi,j,0,fi1,j,0,fi1,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,1min{fi,j,1,fi1,jai,0,fi1,jai,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 0m 中的所有情况,而我只枚举了 a i ∼ m a_i\sim m aim 中的情况,且初步修改时以为是循环顺序不正确,经老师点拨改正。

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 1n3×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)=(r1a+r2c+r3e,r1b+r2d+r3f) ,三个移动后的点如题意,则用 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,r3fi+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,r3fi+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+1fi+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=ir1r2,可省去一维复杂度,降为 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,0fi,v,0+fi1,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,1fi,v,1+fi1,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,0fi,v,0+fi1,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,1fi,v,1+fi1,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 1k,xn2×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 A1Ai 中选的第 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 1lx,in),再枚举选中的第 l − 1 l-1 l1 个数的位置 j j j max ⁡ ( 0 , i − k ) ≤ j < i \max (0,i-k)\le j < i max(0,ik)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,imax(fl,i,fl1,j+Ai)。则答案即为 max ⁡ i = n − k + 1 n f x , i \max\limits_{i=n-k+1}^{n} f_{x,i} i=nk+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,imax(fl,i,fl1,j+Ai) 一式,发现其中 f l − 1 , j f_{l-1,j} fl1,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 ();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值