差分,莫队,带修莫队,题解2023.5.26

差分:一维,时间复杂度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();
    }
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值