每日训练-7.7(差分专题)
1.道路铺设
1.题意
2.知识点
差分
3.思路
维护区间的减可以构造原数组的差分数组,题意要求最后原数组都为0,等价于差分数组都为0。
注意到”需要保证,区间内的每块区域在填充前下陷深度均不为 0 “,这句话则保证了从下标为1开始的
每一段差分数组的正数和大于负数和(相反数),而区间减在差分数组中表现为 a[L] --,a[R + 1] ++,
所以我们只需要存储所有正数的和即为答案(因为正数和大于负数,所以负数可以使用正数的差分操作来中和掉)
4.代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N], tmp[N];
signed main() {
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> tmp[i];
for (int i = 1; i <= n; i ++ ) a[i] = tmp[i] - tmp[i - 1];
int res = 0;
for (int i = 1; i <= n; i ++ ) if (a[i] > 0) res += a[i];
cout << res << endl;
return 0;
}
2.加加减减
1.题意
2.知识点
差分
3.思路
首先构造差分数组,要使原数组所有值相等只需要将差分数组除第一个元素外变为0就能实现。
两种操作分别是
1.a[L] – , a[R + 1] ++
2.a[L] ++, a[R + 1] –
这意味着我们可以通过若干操作使一段区间中的正负数相中和,最终区间内会剩下溢出的一个正数或负数,通过这个数可以对数组头元素加或减 (0 ~ abs(余下的值)),从而产生不同的方案,即方案数等于abs(余值) + 1。
4.代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n;
int a[N], tmp[N];
signed main() {
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> tmp[i];
for (int i = 1; i <= n; i ++ ) a[i] = tmp[i] - tmp[i - 1];
int sum[2] = {0, 0};
for (int i = 2; i <= n; i ++ ) {
if (a[i] > 0) sum[0] += a[i];
else sum[1] -= a[i];
}
cout << max(sum[0], sum[1]) << endl;
cout << abs(sum[0] - sum[1]) + 1 << endl;
return 0;
}
5.小结
原数组所有值相等 <=> 差分数组除第一个元素外 == 0
3.积木大赛
1.题意
2.知识点
chafen
3.思路
与第一题类似
4.代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int tmp[N], a[N];
signed main() {
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> tmp[i];
for (int i = 1; i <= n; i ++ ) a[i] = tmp[i] - tmp[i - 1];
int res = 0;
for (int i = 1; i <= n; i ++ ) if (a[i] > 0) res += a[i];
cout << res << endl;
return 0;
}
4.Fibonacci Additions
1.题意
2.知识点
差分 构造 思维
3.思路
4.代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 300010;
int n, m, MOD;
int A[N], B[N], C[N], D[N];
int fib[N];
char op;
int L, R;
int zero = 0;
void upd(int id, int x) {
if (1 <= id && id <= n) {
zero -= (D[id] == 0);
D[id] += x % MOD;
D[id] = (D[id] + MOD) % MOD;
zero += (D[id] == 0);
}
}
void solve() {
cin >> n >> m >> MOD;
for (int i = 1; i <= n; i ++ ) cin >> A[i];
for (int i = 1; i <= n; i ++ ) cin >> B[i];
for (int i = 1; i <= n; i ++ ) C[i] = A[i] - B[i];
D[1] = C[1] % MOD;
D[2] = (C[2] - C[1] + MOD) % MOD;
for (int i = 3; i <= n; i++)D[i] = (C[i] - C[i - 1] - C[i - 2] + 2 * MOD) % MOD;
fib[1] = 1, fib[2] = 1;
for (int i = 3; i < N; i++)fib[i] = (fib[i - 1] + fib[i - 2]) % MOD;
for (int i = 1; i <= n; i++)zero += (D[i] == 0);
while (m -- ) {
cin >> op >> L >> R;
if (op == 'A') {
upd(L, 1);
upd(R + 1, -fib[R - L + 2]);
upd(R + 2, -fib[R - L + 1]);
}else {
upd(L, -1);
upd(R + 1, fib[R - L + 2]);
upd(R + 2, fib[R - L + 1]);
}
if (zero == n) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
5.Fixed Point Guessing
1.题意
2.知识点
二分 构造 交互
3.思路
通过二分,将数组分为两块,则对于下标区间 [L,R] ,统计:值在 [L,R] 的元素个数:
若下标 [L,R] 内部两数进行交换,则值增加 2。
若下标 [L,R] 的数与其他区间的数进行交换,则值不变。
若下标 [L,R] 的数没有发生交换,则答案增加 1 。
4.代码
#include <bits/stdc++.h>
using namespace std;
const int N=200010;
int n;
bool check(int l,int r) {
printf("? %d %d\n",l,r);
fflush(stdout);
int cnt = 0;
for (int i = 0; i < r - l + 1; i ++ ) {
int x;
cin >> x;
cnt += x >= l && x <= r;
}
return cnt % 2 == 1;
}
void solve() {
cin >> n;
int l = 0, r = n;
while (l+1<r) {
int md=(l+r)>>1;
if (check(1,md)) r=md; else l=md;
}
printf("! %d\n",r);
fflush(stdout);
}
signed main() {
int T;
cin >> T;
while (T -- ) solve();
return 0;
}