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| ∣sx−tx∣ 和 ∣ s y − t y ∣ |s_y-t_y| ∣sy−ty∣ 则不能到达。否则,先移动至能移动到的最高行 m a x l ← s x + ⌊ n − s x k ⌋ × k maxl\gets s_x+\lfloor\frac{n-s_x}{k}\rfloor\times k maxl←sx+⌊kn−sx⌋×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 sx≤tx,需要注意。
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 ai∼aj 的总最小操作次数为 ∑ x = i j max y = 1 m a x , y \sum\limits_{x=i}^{j}\max\limits_{y=1}^{m}a_{x,y} x=i∑jy=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=i∑jy=1maxmax,y≤k 即可。
此时复杂度为 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=i∑jy=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 1≤x<y≤n 使得 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 y0≤y≤y1。
此时时间复杂度上升为 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+1∼ai 中的任一节点,设 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=1∑nj=i+1∑npi,j。
思路:我们先考虑贪心过程,假如当前在 i i i 节点,有如下两种情况:
-
当 j ≤ a i j\le a_i j≤ai 时, p i , j ← 1 p_{i,j}\gets 1 pi,j←1。
-
当 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} fi←j=i+1∑npi,j,思考每个点对 f i f_i fi 的贡献为何:
-
对于 i + 1 ≤ j ≤ n i+1\le j\le n i+1≤j≤n 的所有节点,都至少要走 1 1 1 步,这部分总贡献为 n − i n-i n−i。
-
然后,对于 v i + 1 ≤ j ≤ n v_i+1\le j\le n vi+1≤j≤n 的所有节点,先走到 v i v_i vi 再走最优,有 f v i f_{v_i} fvi 的贡献。
-
但是,对于 v i + 1 ≤ j ≤ a i v_i+1 \le j \le a_i vi+1≤j≤ai 的所有节点,实际只需走 1 1 1 步而不需中转,上一条的贡献 − ( a i − v i ) -(a_i-v_i) −(ai−vi)。
则 f i ← f v i + ( n − i ) − ( a i − v i ) f_i\gets f_{v_i}+(n-i)-(a_i-v_i) fi←fvi+(n−i)−(ai−vi),答案即为 ∑ i = 1 n f i \sum\limits_{i=1}^{n}f_i i=1∑nfi。
现时间复杂度为 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=1∑nfi 时可能超过 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 ();
}