20250223 总结

20250223 总结

A - CodeForces - 1709D

简化题意:有一个 n n n m m m 列的网格, 第 i i i 列有一个 a i a_i ai 行高的障碍。
给定 q q q 组询问 s x , s y , t x , t y , k s_x,s_y,t_x,t_y,k sx,sy,tx,ty,k,每组询问定义一次移动为上下左右任一方向移动 k k k 格,求是否能在若干次操作后从 ( s x , s y ) (s_x,s_y) (sx,sy) 移动至 ( t x , t y ) (t_x,t_y) (tx,ty),且途中不碰到障碍。

思路:首先,因为每次移动必须走 k k k 格,所以如果 k k k 不能整除 ∣ s x − t x ∣ |s_x-t_x| sxtx ∣ s y − t y ∣ |s_y-t_y| syty 则不能到达。否则,先移动至能移动到的最高行 m a x l ← s x + ⌊ n − s x k ⌋ × k maxl\gets s_x+\lfloor\frac{n-s_x}{k}\rfloor\times k maxlsx+knsx×k,如果 max ⁡ i = min ⁡ ( s x , t x ) max ⁡ ( s x , t x ) a i < m a x l \max\limits_{i=\min (s_x,t_x)}^{\max (s_x,t_x)}a_i<maxl i=min(sx,tx)maxmax(sx,tx)ai<maxl,则一定会碰到障碍,不能到达。其余情况均可到达。

现算法时间复杂度为 O ( n 2 ) \Omicron (n^2) O(n2),瓶颈在于求 max ⁡ i = min ⁡ ( s x , t x ) max ⁡ ( s x , t x ) a i \max\limits_{i=\min (s_x,t_x)}^{\max (s_x,t_x)}a_i i=min(sx,tx)maxmax(sx,tx)ai。发现 max ⁡ i = min ⁡ ( s x , t x ) max ⁡ ( s x , t x ) a i \max\limits_{i=\min (s_x,t_x)}^{\max (s_x,t_x)}a_i i=min(sx,tx)maxmax(sx,tx)ai 符合 RMQ 问题的性质,使用 ST 表维护可将时间复杂度降为 O ( n log ⁡ 2 n ) \Omicron (n\log_2 n) O(nlog2n),可以通过,瓶颈在于 ST 表初始化。

细节及难点:注意到式子中我写的是 max ⁡ i = min ⁡ ( s x , t x ) max ⁡ ( s x , t x ) a i \max\limits_{i=\min (s_x,t_x)}^{\max (s_x,t_x)}a_i i=min(sx,tx)maxmax(sx,tx)ai 而不是 max ⁡ i = s x t x a i \max\limits_{i=s_x}^{t_x}a_i i=sxmaxtxai。原因在于题目没有保证 s x ≤ t x s_x\le t_x sxtx,需要注意。

B - CodeForces - 514D

简化题意:有 n n n m m m 项的序列 a i a_i ai,每次操作可以让所有序列的同一项减一,最多操作 k k k 次。当一个序列所有项都 ≤ 0 \le 0 0 则视为被销毁,求最多销毁连续的几个序列,并给出此时对每一项执行了几次操作。

思路:显然,序列 a i ∼ a j a_i\sim a_j aiaj 的总最小操作次数为 ∑ x = i j max ⁡ y = 1 m a x , y \sum\limits_{x=i}^{j}\max\limits_{y=1}^{m}a_{x,y} x=ijy=1maxmax,y,而 max ⁡ y = 1 m a x , y \max\limits_{y=1}^{m}a_{x,y} y=1maxmax,y 满足单调不降性,所以可以二分。枚举 i i i,二分 j j j,每次判断是否满足 ∑ x = i j max ⁡ y = 1 m a x , y ≤ k \sum\limits_{x=i}^{j}\max\limits_{y=1}^{m}a_{x,y}\le k x=ijy=1maxmax,yk 即可。

此时复杂度为 O ( n 2 m log ⁡ 2 n ) \Omicron (n^2m\log_2 n) O(n2mlog2n),不能通过。注意到瓶颈在于求 ∑ x = i j max ⁡ y = 1 m a x , y \sum\limits_{x=i}^{j}\max\limits_{y=1}^{m}a_{x,y} x=ijy=1maxmax,y,显然 max ⁡ y = 1 m a x , y \max\limits_{y=1}^{m}a_{x,y} y=1maxmax,y 可用 ST 表维护,时间复杂度降为 O ( n m log ⁡ 2 n ) \Omicron (nm\log_2 n) O(nmlog2n)

细节及难点:无。

C - CodeForces - 1454F

简化题意:给定一个 n n n 项的序列 a a a,求两个端点 1 ≤ x < y ≤ n 1\le x<y\le n 1x<yn 使得 max ⁡ i = 1 x a i = min ⁡ i = x + 1 y a i = max ⁡ i = x + y + 1 n a i \max\limits_{i=1}^{x}a_i=\min\limits_{i=x+1}^{y}a_i=\max\limits_{i=x+y+1}^{n}a_i i=1maxxai=i=x+1minyai=i=x+y+1maxnai

思路:暴力枚举两个端点时间复杂度 O ( n 2 ) \Omicron (n^2) O(n2) 显然不可行,考虑优化。

注意到题面中的三个式子均满足单调性,我们可以枚举端点 x x x,二分满足 max ⁡ i = 1 x a i = min ⁡ i = x + 1 y a i \max\limits_{i=1}^{x}a_i=\min\limits_{i=x+1}^{y}a_i i=1maxxai=i=x+1minyai 的第一个和最后一个端点 y 0 , y 1 y_0,y_1 y0,y1,并枚举使得 max ⁡ i = 1 x a i = max ⁡ i = x + y + 1 n a i \max\limits_{i=1}^{x}a_i=\max\limits_{i=x+y+1}^{n}a_i i=1maxxai=i=x+y+1maxnai y 0 ≤ y ≤ y 1 y_0\le y\le y_1 y0yy1

此时时间复杂度上升为 O ( n 2 log ⁡ 2 n ) \Omicron (n^2\log_2 n) O(n2log2n),原因在于求 max ⁡ i = 1 x a i , min ⁡ i = x + 1 y a i , max ⁡ i = x + y + 1 n a i \max\limits_{i=1}^{x}a_i,\min\limits_{i=x+1}^{y}a_i,\max\limits_{i=x+y+1}^{n}a_i i=1maxxai,i=x+1minyai,i=x+y+1maxnai 均需 O ( n ) \Omicron (n) O(n) 复杂度。可以用前后缀最大值求 max ⁡ i = 1 x a i , max ⁡ i = x + y + 1 n a i \max\limits_{i=1}^{x}a_i,\max\limits_{i=x+y+1}^{n}a_i i=1maxxai,i=x+y+1maxnai 均需 O ( n ) \Omicron (n) O(n),并用 ST 表维护 min ⁡ i = x + 1 y a i \min\limits_{i=x+1}^{y}a_i i=x+1minyai 即可将时间复杂度降为 O ( n log ⁡ 2 n ) \Omicron (n\log_2 n) O(nlog2n)

存在单个二分的做法。

细节及难点:注意到当 x x x 固定时 min ⁡ i = x + 1 y a i , max ⁡ i = x + y + 1 n a i \min\limits_{i=x+1}^{y}a_i,\max\limits_{i=x+y+1}^{n}a_i i=x+1minyai,i=x+y+1maxnai 满足相同的单调性,所以不能使用错误的思路单考虑 y y y 的取值。

D - CodeForces - 475D

简化题意:给定一个长度为 n n n 的序列 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an q q q 组询问 x 1 , x 2 , ⋯   , x q x_1,x_2,\cdots,x_q x1,x2,,xq,每组询问求有多少组 ( l , r ) (l,r) (l,r) 满足 gcd ⁡ ( a l , a l + 1 , ⋯   , a r ) = x i \gcd (a_l,a_{l+1},\cdots,a_r)=x_i gcd(al,al+1,,ar)=xi

思路: O ( n 2 ) \Omicron (n^2) O(n2) 的暴力显然不可取,考虑优化。

观察 gcd 的性质,我们发现,如果序列中 gcd 要发生变化,则至少变为原 gcd 的一半,从而 gcd 最多变化 log ⁡ 2 \log_2 log2 次。这一性质使我们可以枚举 gcd 起始点,二分,给两变化点间的 gcd 计算贡献,时间复杂度 O ( n log ⁡ 2 n max ⁡ i = 1 n a i ) \Omicron (n\log_2 n\max\limits_{i=1}^{n}a_i) O(nlog2ni=1maxnai)

细节及难点:无。

E - CodeForces - 675E

简化题意:有 n n n 个节点,第 i i i 个节点可以 1 1 1 的代价到达 i + 1 ∼ a i i+1\sim a_i i+1ai 中的任一节点,设 p i , j p_{i,j} pi,j 表示从 i i i 节点到 j j j 节点的最小代价,求 ∑ i = 1 n ∑ j = i + 1 n p i , j \sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}p_{i,j} i=1nj=i+1npi,j

思路:我们先考虑贪心过程,假如当前在 i i i 节点,有如下两种情况:

  1. j ≤ a i j\le a_i jai 时, p i , j ← 1 p_{i,j}\gets 1 pi,j1

  2. j > a i j>a_i j>ai 时,我们一定会到 a i a_i ai 最大的点上去,这样接下来能到达更远的节点。设该点为 v i v_i vi

由第 2 2 2 点考虑 DP。首先设 f i ← ∑ j = i + 1 n p i , j f_i\gets\sum\limits_{j=i+1}^{n}p_{i,j} fij=i+1npi,j,思考每个点对 f i f_i fi 的贡献为何:

  1. 对于 i + 1 ≤ j ≤ n i+1\le j\le n i+1jn 的所有节点,都至少要走 1 1 1 步,这部分总贡献为 n − i n-i ni

  2. 然后,对于 v i + 1 ≤ j ≤ n v_i+1\le j\le n vi+1jn 的所有节点,先走到 v i v_i vi 再走最优,有 f v i f_{v_i} fvi 的贡献。

  3. 但是,对于 v i + 1 ≤ j ≤ a i v_i+1 \le j \le a_i vi+1jai 的所有节点,实际只需走 1 1 1 步而不需中转,上一条的贡献 − ( a i − v i ) -(a_i-v_i) (aivi)

f i ← f v i + ( n − i ) − ( a i − v i ) f_i\gets f_{v_i}+(n-i)-(a_i-v_i) fifvi+(ni)(aivi),答案即为 ∑ i = 1 n f i \sum\limits_{i=1}^{n}f_i i=1nfi

现时间复杂度为 O ( n 2 ) \Omicron (n^2) O(n2),瓶颈在于求 v i v_i vi。注意到 v i v_i vi 符合 RMQ 问题的性质,可用 ST 表维护,时间复杂度降为 O ( n log ⁡ 2 n ) \Omicron (n\log_2 n) O(nlog2n),可以通过,瓶颈在于 ST 表初始化。

细节及难点:一开始我 WA 了两发,查找原因发现计算 f i f_i fi ∑ i = 1 n f i \sum\limits_{i=1}^{n}f_i i=1nfi 时可能超过 int 的范围,需开 long long。


My Code

A

#include <bits/stdc++.h>
using namespace std;

namespace rab {
	int n,m,q,a[200010],lg[200010];

	class ST {
	public:
		int f[200010][20];

		void init () {
			for (int i=1;i<=m;i++)
				f[i][0]=a[i];
			for (int j=1;j<=lg[m];j++)
				for (int i=1;i+(1<<j)-1<=m;i++)
					f[i][j]=max (f[i][j-1],f[i+(1<<j-1)][j-1]);
		}

		int query (int l,int r) {
			int k=lg[r-l+1];
			return max (f[l][k],f[r-(1<<k)+1][k]);
		}
	} st;

	void solve () {
		int sx,sy,fx,fy,k;
		cin>> sx>> sy>> fx>> fy>> k;
		if (abs (sx-fx)%k!=0||abs (sy-fy)%k!=0) cout<< "NO\n";
		else if (sx+(n-sx)/k*k>st.query (min (sy,fy),max (sy,fy))) cout<< "YES\n";
		else cout<< "NO\n";
	}

	int main () {
		cin>> n>> m;
		for (int i=1;i<=m;i++) {
			cin>> a[i];
			if (i!=1) lg[i]=lg[i>>1]+1;
		}
		st.init ();
		cin>> q;
		while (q--) solve ();
		return 0;
	}
}

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 {
	int n,m,k,ml,lg[100001],a[100001][6];
	int tmp[6],ans[6];

	class ST {
	public:
		int f[100001][17][6];

		void init () {
			for (int i=1;i<=n;i++)
				for (int j=1;j<=m;j++)
					f[i][0][j]=a[i][j];
			for (int j=1;j<=lg[n];j++)
				for (int i=1;i+(1<<j)-1<=n;i++)
					for (int k=1;k<=m;k++)
						f[i][j][k]=max (f[i][j-1][k],f[i+(1<<j-1)][j-1][k]);
		}

		int query (int l,int r,int t) {
			int k=lg[r-l+1];
			return max (f[l][k][t],f[r-(1<<k)+1][k][t]);
		}
	} st;

	bool check (int l,int r) {
		int rs=0;
		for (int i=1;i<=m;i++)
			rs+=(tmp[i]=st.query (l,r,i));
		return rs<=k;
	}

	int main () {
		cin>> n>> m>> k;
		for (int i=1;i<=n;i++) {
			for (int j=1;j<=m;j++)
				cin>> a[i][j];
			if (i!=1) lg[i]=lg[i>>1]+1;
		}
		st.init ();
		for (int i=1;i<=n;i++) {
			int l=i,r=n;
			while (l<=r) {
				int mid=l+r>>1;
				if (check (i,mid)) {
					l=mid+1;
					if (mid-i+1>ml) {
						ml=mid-i+1;
						for (int j=1;j<=m;j++)
							ans[j]=tmp[j];
					}
				} else r=mid-1;
			}
		}
		for (int i=1;i<=m;i++) cout<< ans[i]<< " ";
		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;

namespace rab {
	int T,n,lg[200010],a[200010];

	class ST {
	public:
		int f[200010][20],g[200010][20];

		void init () {
			for (int i=1;i<=n;i++)
				f[i][0]=g[i][0]=a[i];
			for (int j=1;j<=lg[n];j++) {
				for (int i=1;i+(1<<j)-1<=n;i++) {
					f[i][j]=min (f[i][j-1],f[i+(1<<j-1)][j-1]);
					g[i][j]=max (g[i][j-1],g[i+(1<<j-1)][j-1]);
				}
			}
		}

		int query (int l,int r,bool t) {
			if (l>r) return -1;
			int k=lg[r-l+1];
			if (t) return min (f[l][k],f[r-(1<<k)+1][k]);
			else return max (g[l][k],g[r-(1<<k)+1][k]);
		}
	} st;

	void solve () {
		cin>> n;
		for (int i=1;i<=n;i++) cin>> a[i];
		st.init ();
		for (int x=1;x<=n;x++) {
			int k0=st.query (1,x,0);
			int l1=x+1,r1=n,p1=0;
			while (l1<=r1) {
				int mid=l1+r1>>1;
				int k1=st.query (x+1,mid,1);
				if (k0>=k1) r1=(p1=mid)-1;
				else l1=mid+1;
			}
			int l2=x+1,r2=n,p2=0;
			while (l2<=r2) {
				int mid=l2+r2>>1;
				int k1=st.query (x+1,mid,1);
				if (k0>k1) r2=mid-1;
				else l2=(p2=mid)+1;
			}
			if (p1>p2||k0!=st.query (x+1,p1,1)||k0!=st.query (x+1,p2,1)) continue;
			int l=p1,r=p2,p=0;
			while (l<=r) {
				int mid=l+r>>1;
				int k2=st.query (mid+1,n,0);
				if (k0<k2) l=mid+1;
				else r=(p=mid)-1;
			}
			if (k0!=st.query (p+1,n,0)) continue;
			cout<< "YES\n"<< x<< " "<< p-x<< " "<< n-p<< "\n";
			return ;
		}
		cout<< "NO\n";
	}

	int main () {
		cin>> T;
		for (int i=2;i<=200000;i++) lg[i]=lg[i>>1]+1;
		while (T--) solve ();
		return 0;
	}
}

int main () {
	ios::sync_with_stdio (0);
	cin.tie (0);cout.tie (0);
	return rab::main ();
}

D

#include <bits/stdc++.h>
using namespace std;

namespace rab {
	int n,q,a[100010],lg[100010];
	unordered_map<int,long long> mp;

	class ST {
	public:
		int f[100010][20];

		void init () {
			for (int i=1;i<=n;i++) f[i][0]=a[i];
			for (int j=1;j<=lg[n];j++)
				for (int i=1;i+(1<<j)-1<=n;i++)
					f[i][j]=__gcd (f[i][j-1],f[i+(1<<j-1)][j-1]);
		}

		int query (int l,int r) {
			int k=lg[r-l+1];
			return __gcd (f[l][k],f[r-(1<<k)+1][k]);
		}
	} st;

	int main () {
		cin>> n;
		for (int i=1;i<=n;i++) {
			cin>> a[i];
			if (i!=1) lg[i]=lg[i>>1]+1;
		}
		st.init ();
		for (int i=1;i<=n;i++) {
			int p=i,g=st.f[i][0];
			while (p<=n&&g!=1) {
				int l=p,r=n,rs=n+1;
				while (l<=r) {
					int mid=l+r>>1;
					if (st.query (i,mid)!=g) r=(rs=mid)-1;
					else l=mid+1;
				}
				mp[g]+=(rs-p);
				g=st.query (i,p=rs);
			}
			if (p!=n+1) mp[1]+=(n-p+1);
		}
		cin>> q;
		while (q--) {
			int x;
			cin>> x;
			if (mp.count (x)) cout<< mp[x]<< "\n";
			else cout<< "0\n";
		}
		return 0;
	}
}

int main () {
	ios::sync_with_stdio (0);
	cin.tie (0);cout.tie (0);
	return rab::main ();
}

E

#include <bits/stdc++.h>
using namespace std;

namespace rab {
	int n,a[200010];
	int lg[200010];
	long long ans,dp[200010];

	int _max (int x,int y) {return a[x]>a[y]? x: y;}

	class ST {
	public:
		int f[200010][20];

		void init () {
			for (int i=1;i<=n;i++) f[i][0]=i;
			for (int j=1;j<=lg[n];j++)
				for (int i=1;i+(1<<j)-1<=n;i++)
					f[i][j]=_max (f[i][j-1],f[i+(1<<j-1)][j-1]);
		}

		int query (int l,int r) {
			int k=lg[r-l+1];
			return _max (f[l][k],f[r-(1<<k)+1][k]);
		}
	} st;

	int main () {
		cin>> n;
		for (int i=1;i<n;i++) cin>> a[i];
		for (int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
		st.init ();
		for (int i=n-1;i>=1;i--) {
			int j=st.query (i+1,a[i]);
			ans+=(dp[i]=dp[j]+n-i+j-a[i]);
		}
		cout<< ans;
		return 0;
	}
}

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、付费专栏及课程。

余额充值