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]加上
k
k
k,所以我们建立差分数组
d
i
f
dif
dif,针对每次计划:
d
i
f
[
l
]
+
=
k
;
d
i
f
[
r
+
1
]
−
=
k
dif[l]+=k;dif[r+1]-=k
dif[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
)
=
x
f(x) = x
f(x)=x差分后
d
i
f
(
x
)
=
f
(
x
)
−
f
(
x
−
1
)
=
1
dif(x) = f(x) - f(x-1) = 1
dif(x)=f(x)−f(x−1)=1变成常数序列,再差分一下就可以搞了,经历了两次差分,我们对于差分数组求两次前缀和就可以了,但是要知道,多次差分的右端点处理比较麻烦,每求一次前缀和就要再次抵消一次本次修改的影响.但是这个题目每次修改的影响范围是
[
i
,
n
]
[i, n]
[i,n]无需考虑右端点.所以,最后做法是:针对每次在起点i的修改我们都执行:
d
i
f
[
i
]
+
=
1
;
dif[i]+=1;
dif[i]+=1;然后
d
i
f
dif
dif求两遍前缀和就得到了原数组.
标程代码:
#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
+
1
r-l+1
r−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.按照重量再次由大到小排序,然后处理出闪耀值前缀最大值,即
m
a
x
x
[
i
]
maxx[i]
maxx[i]为前i个数的闪耀值最大值.
3.针对每次查询a,二分
m
a
x
x
maxx
maxx (
m
a
x
x
maxx
maxx数组显然是非递减的) 找到第一个大于等于a的标号,则这个宝石的重量值就是最小重量值(依据贪心思想,比它靠前的
m
a
x
x
[
i
]
maxx[i]
maxx[i]无法满足要求,比它靠后的重量值比它大),然后得出所求最小重量值
a
n
s
w
answ
answ后对
a
n
s
w
answ
answ的vector进行二分,找到这个重量值下又多少闪耀值大于等于a的,得出数量.
最后的时间复杂度,每次查询
O
(
l
o
g
n
)
O(logn)
O(logn)预处理是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)所以最后时间复杂度为
O
(
(
m
+
n
)
l
o
g
n
)
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)进行分析,发现是首项为
6
k
\frac{6}{k}
k6公差为
3
k
\frac{3}{k}
k3的等差数列,于是有通向公式:
g
(
x
)
=
3
(
n
+
1
)
k
g(x)=\frac{3(n+1)}{k}
g(x)=k3(n+1),计算
h
(
x
)
=
f
(
x
)
∗
g
(
x
)
+
a
=
3
k
(
n
2
+
n
)
2
+
a
h(x)=f(x)*g(x)+a=\frac{3k(n^2+n)}{2}+a
h(x)=f(x)∗g(x)+a=23k(n2+n)+a,因为训练营的成绩值是所有人成绩值累加值,故拆成两部分
h
1
(
x
)
=
3
k
(
n
2
+
n
)
2
h_1(x)=\frac{3k(n^2+n)}{2}
h1(x)=23k(n2+n)和
h
2
(
x
)
=
a
h_2(x)=a
h2(x)=a分别累加对成绩无影响(加法交换律)
h
2
(
x
)
h_2(x)
h2(x)是常数列可以直接用差分处理.对
h
1
(
x
)
h_1(x)
h1(x)进行两次差分,
h
1
′
(
x
)
=
h
1
(
x
)
−
h
1
(
x
−
1
)
h_1^{'}(x)=h_1(x)-h_1(x-1)
h1′(x)=h1(x)−h1(x−1)紧接着
h
1
′
′
(
x
)
=
h
1
′
(
x
)
−
h
1
′
(
x
−
1
)
=
3
k
h_1^{''}(x)=h_1^{'}(x)-h_1^{'}(x-1)=3k
h1′′(x)=h1′(x)−h1′(x−1)=3k为常数列,且每个人的影响范围是
[
i
,
n
]
[i,n]
[i,n]不用考虑右端点.我们分别计算好
h
1
(
x
)
h_1(x)
h1(x)和
h
2
(
x
)
h_2(x)
h2(x)在n天的累计值,然后累加起来的
s
u
m
sum
sum数组就是每天训练营的成绩值,然后
s
u
m
sum
sum数组排序,二分就可以得出答案.
修改一共为
O
(
m
)
O(m)
O(m)后续处理是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),每次查询都是
O
(
l
o
g
n
)
O(logn)
O(logn)所以最终时间复杂度为
O
(
m
+
(
n
+
q
)
l
o
g
n
)
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;
}