A.zx学长的施工队1
解题思路在视频中已给出,
标程代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e5+5;
int a[N], sum[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
sum[i] = sum[i-1] + a[i];
int m;
cin >> m;
while(m--)
{
int l, r;
bool flag = 0;
scanf("%d%d", &l, &r);
if (sum[r] - sum[l-1] == r - l + 1) puts("0");
else puts("1");
}
return 0;
}
B.zx学长的施工队2
解题思路在视频中已给出,
标程代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e5+5;
int a[N], dif[N];
int main()
{
int n, m;
while(cin >> n >> m)
{
memset(dif, 0, sizeof(dif));
while(m--)
{
int l, r;
scanf("%d%d", &l, &r);
dif[l]++;
dif[r+1]--;
}
bool flag = 0;
for (int i = 1; i <= n; i++)
{
a[i] = a[i-1] + dif[i];
if (a[i] == 0)
{
flag = 1;
break;
}
}
if (flag) puts("0");
else puts("1");
}
return 0;
}
C.zx学长的口罩支援计划
这个题是一道非常裸的差分题,每次计划都是在区间[l,r][l, r][l,r]加上kkk,所以我们建立差分数组difdifdif,针对每次计划:
dif[l]+=k;dif[r+1]−=kdif[l]+=k;dif[r+1]-=kdif[l]+=k;dif[r+1]−=k
最后求一边前缀和,即可得出答案.
标程代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e5+5;
ll a[N], dif[N];
int main()
{
int n, m;
cin >> n >> m;
while(m--)
{
int l, r;
ll k;
scanf("%d%d%lld", &l, &r, &k);
dif[l] += k;
dif[r+1] -= k;
}
for (int i = 1; i <= n; i++)
a[i] = a[i-1] + dif[i];
bool flag = 1;
for (int i = 1; i <= n; i++)
{
if (flag) flag = 0;
else printf(" ");
printf("%lld", a[i]);
}
puts("");
return 0;
}
D.zx学长的差分教学
每次修改都是从一个起点开始,向后加一个首项为1,公差为1的等差序列:f(x)=xf(x) = xf(x)=x差分后dif(x)=f(x)−f(x−1)=1dif(x) = f(x) - f(x-1) = 1dif(x)=f(x)−f(x−1)=1变成常数序列,再差分一下就可以搞了,经历了两次差分,我们对于差分数组求两次前缀和就可以了,但是要知道,多次差分的右端点处理比较麻烦,每求一次前缀和就要再次抵消一次本次修改的影响.但是这个题目每次修改的影响范围是[i,n][i, n][i,n]无需考虑右端点.所以,最后做法是:针对每次在起点i的修改我们都执行:dif[i]+=1;dif[i]+=1;dif[i]+=1;然后difdifdif求两遍前缀和就得到了原数组.
标程代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e5+5;
ll a[N], dif[N];
int main()
{
int n, m;
cin >> n >> m;
while(m--)
{
int l;
scanf("%d", &l);
dif[l]++;
}
for (int i = 1; i <= n; i++)
a[i] = a[i-1] + dif[i];
for (int i = 1; i <= n; i++)
a[i] += a[i-1];
bool flag = 1;
for (int i = 1; i <= n; i++)
{
if (flag) flag = 0;
else printf(" ");
printf("%lld", a[i]);
}
puts("");
return 0;
}
E.zx学长的幸运线段
这个题并非传统的差分,就拿样例来说,它其实是这么一种情况

这时候第二次查询[1,3][1, 3][1,3]的时候,显然没有其中的[2,3][2, 3][2,3]没有覆盖到,但是如果按照传统差分,就会被判为完全覆盖,所以我们可以看出:一个点被覆盖和一段线段被覆盖是不一样的.所以我们的做法是边化点,让每个点代替它左边的区间段,例如3号点代表的是[2,3][2, 3][2,3]这个区间,这样我们修改时考虑:比如说样例中的第三个线段[3,5][3, 5][3,5]它覆盖的其实是4和5两个点代表的区间段,所以我们不修改[3,5][3, 5][3,5],而是修改[4,5][4, 5][4,5],这样每次查询或者修改[l,r][l, r][l,r]我们都变换成[l+1,r][l+1, r][l+1,r],然后正常差分即可.差分方法:每次修改区间,我们都让这个区间的数值加一,这样最后求一边前缀和,得出的是每个点的覆盖次数,然后我们一遍变换,将所有覆盖次数大于0的(被覆盖过的)都变成1,然后再求前缀和,这样每次查询区间[l,r][l, r][l,r]只要它的区间元素和为r−l+1r-l+1r−l+1我们就认定是全部被覆盖(类比A题).
标程代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e6+5;
int a[N], dif[N];
int main()
{
int n, m;
cin >> m;
while(m--)
{
int l, r;
scanf("%d%d", &l, &r);
dif[l+1]++;
dif[r+1]--;
}
for (int i = 1; i < N; i++)
a[i] = a[i-1] + dif[i];
for (int i = 1; i < N; i++)
if (a[i]) a[i] = 1;
for (int i = 1; i < N; i++)
a[i] += a[i-1];
int q;
cin >> q;
while(q--)
{
int l, r;
scanf("%d%d", &l, &r);
if (a[r] - a[l] == r - l)
puts("GoodLuck!");
else puts("No!");
}
return 0;
}
F.zx学长的抓捕行动
虽然题目比较长,但是是比较裸的的二维差分,我们每次按照监控,都把这个监控所能探测的子矩阵中元素都加一,然后求一边前缀和,就是这个矩阵每个元素被覆盖的次数,然后我们再次求前缀和,如果小偷作案的所有矩形中的元素的和都是0,则说明没有监控被覆盖到,就抓不到,否则就会被捉到.
标程代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 2e3+5;
int a[N][N], dif[N][N];
int main()
{
int n, m;
while(cin >> n >> m)
{
memset(dif, 0, sizeof(dif));
int q;
cin >> q;
while(q--)
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
dif[x1][y1]++;
dif[x2+1][y1]--;
dif[x1][y2+1]--;
dif[x2+1][y2+1]++;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + dif[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1];
int p;
cin >> p;
bool flag = 0;
while(p--)
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
if (a[x2][y2] - a[x1-1][y2] - a[x2][y1-1] + a[x1-1][y1-1])
{
flag = 1;
continue;
}
}
if (flag) puts("YES");
else puts("NO");
}
return 0;
}
G.zx学长的QDU合作
较为简单的题,正常差分,最后比较一下值是不是大于了上限,如果大于上限,就赋值为上限,
标程代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e5+5;
int a[N], b[N], dif[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
int m;
cin >> m;
while(m--)
{
int l, r;
scanf("%d%d", &l, &r);
dif[l]++;
dif[r+1]--;
}
for (int i = 1; i <= n; i++)
dif[i] += dif[i-1], a[i] += dif[i];
for (int i = 1; i <= n; i++)
if (a[i] > b[i]) a[i] = b[i];
bool flag = 1;
for (int i = 1; i <= n; i++)
{
if (flag) flag = 0;
else printf(" ");
printf("%d", a[i]);
}
puts("");
return 0;
}
H.zx学长的蓝桥原题
解题思路在视频中已给出,
标程代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e5+5;
ll a[N], col[N];
int main()
{
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
for (int i = 1; i <= n; i++)
{
a[i] += a[i-1];
col[a[i]%k]++;
}
ll cnt = 0;
for (int i = 0; i < k; i++)
cnt += col[i] * (col[i] - 1) / 2;
cnt += col[0];
printf("%lld\n", cnt);
return 0;
}
I.zx学长的宝石探险
1.先按照闪耀值由大到小排序,每个重量值建立一个vector,将每个闪耀值压入对应重量值的vector中.
2.按照重量再次由大到小排序,然后处理出闪耀值前缀最大值,即maxx[i]maxx[i]maxx[i]为前i个数的闪耀值最大值.
3.针对每次查询a,二分maxxmaxxmaxx (maxxmaxxmaxx数组显然是非递减的) 找到第一个大于等于a的标号,则这个宝石的重量值就是最小重量值(依据贪心思想,比它靠前的maxx[i]maxx[i]maxx[i]无法满足要求,比它靠后的重量值比它大),然后得出所求最小重量值answanswansw后对answanswansw的vector进行二分,找到这个重量值下又多少闪耀值大于等于a的,得出数量.
最后的时间复杂度,每次查询O(logn)O(logn)O(logn)预处理是O(nlogn)O(nlogn)O(nlogn)所以最后时间复杂度为O((m+n)logn)O((m+n)logn
)O((m+n)logn)
标程代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e6+5;
vector<int> w[N];
struct Node
{
int w, s;
}a[N];
int maxx[N];
bool cmp1(const Node &a, const Node &b) {return a.s < b.s;}
bool cmp2(const Node &a, const Node &b) {return a.w < b.w;}
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i].s);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i].w);
sort(a+1, a+n+1, cmp1);
for (int i = 1; i <= n; i++)
w[a[i].w].push_back(a[i].s);
sort(a+1, a+n+1, cmp2);
for (int i = 1; i <= n; i++)
maxx[i] = max(maxx[i-1], a[i].s);
int q;
cin >> q;
while(q--)
{
int x;
scanf("%d", &x);
int pos = lower_bound(maxx+1, maxx+n+1, x) - maxx;
if (pos > n)
{
puts("-1");
continue;
}
int answ = a[pos].w;
int l = 0, r = w[answ].size();
while(l <= r)
{
int mid = (l + r) >> 1;
if (w[answ][mid] >= x) r = mid-1;
else l = mid+1;
}
int ansn = w[answ].size() - l;
printf("%d %d\n", answ, ansn);
}
return 0;
}
J.zx学长的ap训练营
对原始g(x)g(x)g(x)进行分析,发现是首项为6k\frac{6}{k}k6公差为3k\frac{3}{k}k3的等差数列,于是有通向公式: g(x)=3(n+1)kg(x)=\frac{3(n+1)}{k}g(x)=k3(n+1),计算h(x)=f(x)∗g(x)+a=3k(n2+n)2+ah(x)=f(x)*g(x)+a=\frac{3k(n^2+n)}{2}+ah(x)=f(x)∗g(x)+a=23k(n2+n)+a,因为训练营的成绩值是所有人成绩值累加值,故拆成两部分h1(x)=3k(n2+n)2h_1(x)=\frac{3k(n^2+n)}{2}h1(x)=23k(n2+n)和h2(x)=ah_2(x)=ah2(x)=a分别累加对成绩无影响(加法交换律)
h2(x)h_2(x)h2(x)是常数列可以直接用差分处理.对h1(x)h_1(x)h1(x)进行两次差分,h1′(x)=h1(x)−h1(x−1)h_1^{'}(x)=h_1(x)-h_1(x-1)h1′(x)=h1(x)−h1(x−1)紧接着h1′′(x)=h1′(x)−h1′(x−1)=3kh_1^{''}(x)=h_1^{'}(x)-h_1^{'}(x-1)=3kh1′′(x)=h1′(x)−h1′(x−1)=3k为常数列,且每个人的影响范围是[i,n][i,n][i,n]不用考虑右端点.我们分别计算好h1(x)h_1(x)h1(x)和h2(x)h_2(x)h2(x)在n天的累计值,然后累加起来的sumsumsum数组就是每天训练营的成绩值,然后sumsumsum数组排序,二分就可以得出答案.
修改一共为O(m)O(m)O(m)后续处理是O(nlogn)O(nlogn)O(nlogn),每次查询都是O(logn)O(logn)O(logn)所以最终时间复杂度为O(m+(n+q)logn)O(m+(n+q)logn)O(m+(n+q)logn)
标称代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e5+5;
ll sum[N], dif[N];
int main()
{
int n, m;
cin >> n >> m;
while(m--)
{
ll l, k, a;
scanf("%lld%lld%lld", &l, &k, &a);
sum[l] += a;
dif[l] += 3 * k;
}
for (int i = 1; i <= n; i++)
{
sum[i] += sum[i-1];
dif[i] += dif[i-1];
}
for (int i = 1; i <= n; i++) dif[i] += dif[i-1];
for (int i = 1; i <= n; i++) dif[i] += dif[i-1];
for (int i = 1; i <= n; i++) sum[i] += dif[i];
sort(sum+1, sum+n+1);
int q;
cin >> q;
while(q--)
{
ll x;
scanf("%lld", &x);
int pos = lower_bound(sum+1, sum+n+1, x) - sum;
printf("%d\n", n - pos + 1);
}
return 0;
}
本篇博客详细解析了zx学长的多个经典算法题目,涵盖了差分数组、二维差分、等差数列、贪心算法、排序、二分查找、前缀和等核心算法概念。通过具体题目的实现代码,深入浅出地讲解了算法的设计思路和实现细节。

375

被折叠的 条评论
为什么被折叠?



