20250223 总结
20250223 总结
A - CodeForces - 1709D
简化题意:有一个 \(n\) 行 \(m\) 列的网格, 第 \(i\) 列有一个 \(a_i\) 行高的障碍。
给定 \(q\) 组询问 \(s_x,s_y,t_x,t_y,k\),每组询问定义一次移动为上下左右任一方向移动 \(k\) 格,求是否能在若干次操作后从 \((s_x,s_y)\) 移动至 \((t_x,t_y)\),且途中不碰到障碍。
思路:首先,因为每次移动必须走 \(k\) 格,所以如果 \(k\) 不能整除 \(|s_x-t_x|\) 和 \(|s_y-t_y|\) 则不能到达。否则,先移动至能移动到的最高行 \(maxl\gets s_x+\lfloor\frac{n-s_x}{k}\rfloor\times k\),如果 \(\max\limits_{i=\min (s_x,t_x)}^{\max (s_x,t_x)}a_i<maxl\),则一定会碰到障碍,不能到达。其余情况均可到达。
现算法时间复杂度为 \(\Omicron (n^2)\),瓶颈在于求 \(\max\limits_{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\) 符合 RMQ 问题的性质,使用 ST 表维护可将时间复杂度降为 \(\Omicron (n\log_2 n)\),可以通过,瓶颈在于 ST 表初始化。
细节及难点:注意到式子中我写的是 \(\max\limits_{i=\min (s_x,t_x)}^{\max (s_x,t_x)}a_i\) 而不是 \(\max\limits_{i=s_x}^{t_x}a_i\)。原因在于题目没有保证 \(s_x\le t_x\),需要注意。
B - CodeForces - 514D
简化题意:有 \(n\) 个 \(m\) 项的序列 \(a_i\),每次操作可以让所有序列的同一项减一,最多操作 \(k\) 次。当一个序列所有项都 \(\le 0\) 则视为被销毁,求最多销毁连续的几个序列,并给出此时对每一项执行了几次操作。
思路:显然,序列 \(a_i\sim a_j\) 的总最小操作次数为 \(\sum\limits_{x=i}^{j}\max\limits_{y=1}^{m}a_{x,y}\),而 \(\max\limits_{y=1}^{m}a_{x,y}\) 满足单调不降性,所以可以二分。枚举 \(i\),二分 \(j\),每次判断是否满足 \(\sum\limits_{x=i}^{j}\max\limits_{y=1}^{m}a_{x,y}\le k\) 即可。
此时复杂度为 \(\Omicron (n^2m\log_2 n)\),不能通过。注意到瓶颈在于求 \(\sum\limits_{x=i}^{j}\max\limits_{y=1}^{m}a_{x,y}\),显然 \(\max\limits_{y=1}^{m}a_{x,y}\) 可用 ST 表维护,时间复杂度降为 \(\Omicron (nm\log_2 n)\)。
细节及难点:无。
C - CodeForces - 1454F
简化题意:给定一个 \(n\) 项的序列 \(a\),求两个端点 \(1\le x<y\le n\) 使得 \(\max\limits_{i=1}^{x}a_i=\min\limits_{i=x+1}^{y}a_i=\max\limits_{i=x+y+1}^{n}a_i\)。
思路:暴力枚举两个端点时间复杂度 \(\Omicron (n^2)\) 显然不可行,考虑优化。
注意到题面中的三个式子均满足单调性,我们可以枚举端点 \(x\),二分满足 \(\max\limits_{i=1}^{x}a_i=\min\limits_{i=x+1}^{y}a_i\) 的第一个和最后一个端点 \(y_0,y_1\),并枚举使得 \(\max\limits_{i=1}^{x}a_i=\max\limits_{i=x+y+1}^{n}a_i\) 的 \(y_0\le y\le y_1\)。
此时时间复杂度上升为 \(\Omicron (n^2\log_2 n)\),原因在于求 \(\max\limits_{i=1}^{x}a_i,\min\limits_{i=x+1}^{y}a_i,\max\limits_{i=x+y+1}^{n}a_i\) 均需 \(\Omicron (n)\) 复杂度。可以用前后缀最大值求 \(\max\limits_{i=1}^{x}a_i,\max\limits_{i=x+y+1}^{n}a_i\) 均需 \(\Omicron (n)\),并用 ST 表维护 \(\min\limits_{i=x+1}^{y}a_i\) 即可将时间复杂度降为 \(\Omicron (n\log_2 n)\)。
存在单个二分的做法。
细节及难点:注意到当 \(x\) 固定时 \(\min\limits_{i=x+1}^{y}a_i,\max\limits_{i=x+y+1}^{n}a_i\) 满足相同的单调性,所以不能使用错误的思路单考虑 \(y\) 的取值。
D - CodeForces - 475D
简化题意:给定一个长度为 \(n\) 的序列 \(a_1,a_2,\cdots,a_n\) 和 \(q\) 组询问 \(x_1,x_2,\cdots,x_q\),每组询问求有多少组 \((l,r)\) 满足 \(\gcd (a_l,a_{l+1},\cdots,a_r)=x_i\)。
思路:\(\Omicron (n^2)\) 的暴力显然不可取,考虑优化。
观察 gcd 的性质,我们发现,如果序列中 gcd 要发生变化,则至少变为原 gcd 的一半,从而 gcd 最多变化 \(\log_2\) 次。这一性质使我们可以枚举 gcd 起始点,二分,给两变化点间的 gcd 计算贡献,时间复杂度 \(\Omicron (n\log_2 n\max\limits_{i=1}^{n}a_i)\)。
细节及难点:无。
E - CodeForces - 675E
简化题意:有 \(n\) 个节点,第 \(i\) 个节点可以 \(1\) 的代价到达 \(i+1\sim a_i\) 中的任一节点,设 \(p_{i,j}\) 表示从 \(i\) 节点到 \(j\) 节点的最小代价,求 \(\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}p_{i,j}\)。
思路:我们先考虑贪心过程,假如当前在 \(i\) 节点,有如下两种情况:
-
当 \(j\le a_i\) 时,\(p_{i,j}\gets 1\)。
-
当 \(j>a_i\) 时,我们一定会到 \(a_i\) 最大的点上去,这样接下来能到达更远的节点。设该点为 \(v_i\)。
由第 \(2\) 点考虑 DP。首先设 \(f_i\gets\sum\limits_{j=i+1}^{n}p_{i,j}\),思考每个点对 \(f_i\) 的贡献为何:
-
对于 \(i+1\le j\le n\) 的所有节点,都至少要走 \(1\) 步,这部分总贡献为 \(n-i\)。
-
然后,对于 \(v_i+1\le j\le n\) 的所有节点,先走到 \(v_i\) 再走最优,有 \(f_{v_i}\) 的贡献。
-
但是,对于 \(v_i+1 \le j \le a_i\) 的所有节点,实际只需走 \(1\) 步而不需中转,上一条的贡献 \(-(a_i-v_i)\)。
则 \(f_i\gets f_{v_i}+(n-i)-(a_i-v_i)\),答案即为 \(\sum\limits_{i=1}^{n}f_i\)。
现时间复杂度为 \(\Omicron (n^2)\),瓶颈在于求 \(v_i\)。注意到 \(v_i\) 符合 RMQ 问题的性质,可用 ST 表维护,时间复杂度降为 \(\Omicron (n\log_2 n)\),可以通过,瓶颈在于 ST 表初始化。
细节及难点:一开始我 WA 了两发,查找原因发现计算 \(f_i\) 和 \(\sum\limits_{i=1}^{n}f_i\) 时可能超过 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 ();
}