差分:一维,时间复杂度o(n)
操作就是利用前缀和,将目标范围内的数进行累加
求多次进行区间修改后的数组
void insert(int l, int r, int c) //差分函数
{
s[l] += c;
s[r + 1] -= c;
}
二维
void insert(int x1, int x2, int y1, int y2, int c)
{//点(x1,y1) 在 (x2,y2)的左上方(或重合)
b[x1][y1] += c;
b[x2 + 1][y2] -= c;
b[x2][y2 + 1] -= c;
b[x2][y2] += c;
}
输入一个长度为n的整数序列。
接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。
请你输出进行完所有操作后的序列。
输入格式
第—行包含两个整数n和m。
第二行包含n个整数,表示整数序列。
接下来m行,每行包含三个整数l, r, c,表示一个操作。
输出格式
共—行,包含n个整数,表示最终序列。数据范围
1 ≤n, m ≤100000, 1 < l < r ≤n, - 1000 < c≤1000,
-1000≤整数序列中元素的值≤1000
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast)
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define ms(x,y) memset(x,y,sizeof x)
#define int long long
const int maxn=2e5+10,INF = 1e18 ;
using namespace std;
int a[maxn];
int s[maxn];
void insert(int l, int r, int c){
s[l] += c;
s[r + 1] -= c;
}
void solve(){
int n, m;
cin >> n>>m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
insert(i, i, a[i]);
}
for (int i = 1; i <= m; i++) {
int l, r, m;
cin >> l >> r >> m;
insert(l, r, m);
}
for (int i = 1; i <= n; i++) { //操作就是利用前缀和,将目标范围内的数进行累加
s[i] += s[i - 1];
}
for (int i = 1; i <= n; i++) {
cout << s[i] << ' ';
}
}
signed main()
{
ios::sync_with_stdio(false);
solve();
}
总结:若每一次操作都遍历,则时间复杂度大大上升,而利用差分则可o(n)解决
CodeForces - 296C Greg and Array?(双差分)
思路:一个差分维护询问(询问指将l,r中的操作编号执行一次相当离线操作),另一个差分维护原数组操作
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast)
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define ms(x,y) memset(x,y,sizeof x)
#define int long long
const int maxn=1e5+10,INF = 1e18 ;
using namespace std;
int a[maxn];
int s[maxn];
int cnt[maxn];
int op[maxn][5];
void insert(int l, int r, int c) { //维护元素组操作
s[l] += c;
s[r + 1] -= c;
}
void insert1(int l, int r, int c) { //维护原数组操作
cnt[l] += c;
cnt[r + 1] -= c;
}
void solve(){
int n, m,k;
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
insert(i, i, a[i]);
}
for (int i = 1; i <= m; i++) {
cin >> op[i][1] >> op[i][2] >> op[i][3];
}
for (int i = 1; i <= k; i++) { //
int l, r;
cin >> l >> r;
insert1(l, r, 1);
}
for (int i = 1; i <= m; i++) {
cnt[i] += cnt[i - 1];
}
for (int i = 1; i <= m; i++) {
insert(op[i][1], op[i][2], op[i][3] * cnt[i]);
}
for (int i = 1; i <= n; i++) {
s[i] += s[i - 1];
}
for (int i = 1; i <= n; i++) {
cout << s[i] << ' ';
}
}
signed main()
{
ios::sync_with_stdio(false);
solve();
}
莫队
普通莫队:解决区间查询等问题的离线算法,基于分块思想,复杂度为O(n根号n)
SP3267 DQUERY - D - query
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast)
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define ms(x,y) memset(x,y,sizeof x)
#define int long long
const int maxn=1e6+10,INF = 1e18 ;
using namespace std;
inline int read() //快读
{
int f = 1;
int x = 0;
char c = getchar();
while (c < '0' || c>'9') {
if (c == '-') {
f = -f;
}
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
inline void printi(int x) //快写
{
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) {
printi(x / 10);
}
putchar(x % 10 + '0');
}
int aa[maxn], cnt[maxn], belong[maxn];
int ans[maxn];
int n, m, Size, bnum, now;
void add(int pos) { //增
if (!cnt[aa[pos]]) ++now;
++cnt[aa[pos]];
}
void del(int pos) { //删
--cnt[aa[pos]];
if (!cnt[aa[pos]]) --now;
}
struct query {
int l, r, id;
} q[maxn]; //把查询区间按照左端点所在块的序号排个序,如果左端点所在块相同,再按右端点排序。
int cmp(query a, query b) { //奇偶性排序,对于左端点在同一奇数块的区间,右端点按升序排列,反之降序
return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
}
void solve(){
n = read();
Size = sqrt(n);
bnum = ceil((double)n / Size); //ceil () 函数用于求不小于 x 的最小整数,也即向上取整
for (int i = 1; i <= bnum; ++i) {
for (int j = (i - 1) * Size + 1; j <= i * Size; ++j) {
belong[j] = i;
}
}
for (int i = 1; i <= n; ++i) aa[i] = read();
m = read();
for (int i = 1; i <= m; ++i) {
q[i].l = read(), q[i].r = read();
q[i].id = i;
}
sort(q + 1, q + m + 1, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; ++i) {
int ql = q[i].l, qr = q[i].r;
/* while (l < ql) now -= !--cnt[aa[l++]]; //移动指针的常数压缩
while (l > ql) now += !cnt[aa[--l]]++;
while (r < qr) now += !cnt[aa[++r]]++;
while (r > qr) now -= !--cnt[aa[r--]];*/
while (l < ql) del(l++);
while (l > ql) add(--l);
while (r < qr) add(++r);
while (r > qr) del(r--);
ans[q[i].id] = now;
}
for (int i = 1; i <= m; ++i)
printi(ans[i]), putchar('\n');
}
signed main()
{
ios::sync_with_stdio(false);
solve();
}
带修莫队 :时间复杂度o(n^(5/3))
Luogu P1903[国家集训队]数颜色 / 维护队列
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast)
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define ms(x,y) memset(x,y,sizeof x)
const int maxn= 1e7+10,maxc=1e7+10,INF = 1e18 ;
using namespace std;
int a[maxn], cnt[maxc], ans[maxn], belong[maxn];
inline int read() //快读
{
int f = 1;
int x = 0;
char c = getchar();
while (c < '0' || c>'9') {
if (c == '-') {
f = -f;
}
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
struct query { //查询
int l, r, time, id;
} q[maxn];
struct modify { //修改
int pos, color, last;
} c[maxn];
int cntq, cntc, n, m, Size, bnum;
int cmp(query a, query b) { //只有当当前区间和查询区间左右端点、时间戳均重合时,才认定区间完全重合
return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.r] ^ belong[b.r]) ? belong[a.r] < belong[b.r] : a.time < b.time);
}
void solve(){
n = read(), m = read();
Size = pow(n, 2.0 / 3.0); //块取n^(2/3)优于取根号n
bnum = ceil((double)n / Size);
for (int i = 1; i <= bnum; ++i)
for (int j = (i - 1) * Size + 1; j <= i * Size; ++j) belong[j] = i;
for (int i = 1; i <= n; ++i)
a[i] = read();
for (int i = 1; i <= m; ++i) {
char opt[100];
scanf("%s", opt);
if (opt[0] == 'Q') {
q[++cntq].l = read();
q[cntq].r = read();
q[cntq].time = cntc;
q[cntq].id = cntq;
}
else if (opt[0] == 'R') {
c[++cntc].pos = read();
c[cntc].color = read();
}
}
sort(q + 1, q + cntq + 1, cmp);
int l = 1, r = 0, time = 0, now = 0;
for (int i = 1; i <= cntq; ++i) {
int ql = q[i].l, qr = q[i].r, qt = q[i].time;
while (l < ql) now -= !--cnt[a[l++]];
while (l > ql) now += !cnt[a[--l]]++;
while (r < qr) now += !cnt[a[++r]]++;
while (r > qr) now -= !--cnt[a[r--]];
while (time < qt) {
++time;
if (ql <= c[time].pos && c[time].pos <= qr) now -= !--cnt[a[c[time].pos]] - !cnt[c[time].color]++;
swap(a[c[time].pos], c[time].color);
}
while (time > qt) {
if (ql <= c[time].pos && c[time].pos <= qr) now -= !--cnt[a[c[time].pos]] - !cnt[c[time].color]++;
swap(a[c[time].pos], c[time].color);
--time;
}
ans[q[i].id] = now;
}
for (int i = 1; i <= cntq; ++i)
printf("%d\n", ans[i]);
}
signed main()
{
ios::sync_with_stdio(false);
solve();
}
题解
C:Hamiltonian Wall
题意:能否一笔把B格子全部画完(一笔画完)
思路:使用dp,记录最后一个B位置和第一个B位置,若最后一列的B能否由开头转移过来,则YES,否则NO
转移的策略有三种,
1.一列有两个B,转移位置与上一列B转移位置相异;
2.一列有一个B,位置与上一列(一个B)位置相同
3.一列有一个B,位置与上一列(两个B)转移位置相同
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast)
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define ms(x,y) memset(x,y,sizeof x)
#define int long long
const int maxn=2e5+10,INF = 1e18 ;
using namespace std;
int a[3][maxn];
int f[maxn][3];
void solve(){
int n;
cin >> n;
for (int i = 1;i<=2; i++) {
for (int j = 1; j <= n; j++) {
char c;
cin >> c;
if (c == 'B')
a[i][j] = 1;
else
a[i][j] = 0;
}
}
int Min = n+1, Max = -1, flag = 0;
for (int i = 1; i <= n; i++) {
f[i][1] = 0;
f[i][2] = 0;
if (a[1][i] || a[2][i]) {
Min = min(Min, i);
Max = max(Max, i);
flag = 1;
}
}
if (!flag) {
cout << "YES" << '\n';
return;
}
f[Min][1] = (a[1][Min] == 1);
f[Min][2] = (a[2][Min] == 1);
for (int i = Min+1; i <= Max; i++) {
if (a[1][i] == 1 && a[2][i] == 1) {
f[i][1] = f[i - 1][2];
f[i][2] = f[i - 1][1];
}
if (a[1][i] == 1 && a[2][i] == 0) {
f[i][1] = f[i - 1][1];
}
if (a[1][i] == 0 && a[2][i] == 1) {
f[i][2] = f[i - 1][2];
}
}
if (f[Max][1] || f[Max][2]) {
cout << "YES" << '\n';
}
else {
cout << "NO" << '\n';
}
}
signed main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--) {
solve();
}
}
Codeforces Round #834 (Div. 3) C.Thermostat
思路:五种情况
1.直接到达
2.abs(a-b)>=x;一步
3.b - l >= x && r - b >= x//可以从l,也可以从r到达
4.b - l >= x//可以从l到达
5.r - b >= x //可以从r到达
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast)
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define ms(x,y) memset(x,y,sizeof x)
#define int long long
const int maxn=2e5+10,INF = 1e18 ;
using namespace std;
int a[maxn];
void solve(){
int l, r, a, b, x;
cin >> l >> r >> x;
cin >> a >> b;
if (a == b) {
cout << 0 << '\n';
return;
}
if (abs(b - a) >= x) {
cout << 1 << '\n';
return;
}
if (b - l >= x && r - b >= x) { //可以从l,也可以从r到达
if (a - l >= x || r - a >= x) cout << 2 << '\n';
else cout << -1 << '\n';
}
else if (b - l >= x) {
if (a - l >= x) cout << 2 << '\n';
else if (r - a >= x)cout << 3 << '\n'; //a不可以直接到达l,但是可以到达r,可以先到达r,再从r往l,然后到达b
else cout << -1 << '\n';
}
else if (r - b >= x) {
if (r - a >= x)cout << 2 << '\n';
else if (a-l >= x)cout << 3 << '\n';
else cout << -1 << '\n';
}
else {
cout << -1 << '\n';
}
}
signed main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--) {
solve();
}
}