UVA 12345 Dynamic len(带修莫队)

本文介绍了一种改进的莫队算法——带修莫队,适用于动态查询问题。通过增加查询次数维度,采用离线处理策略,实现高效单点修改与区间查询。文章详细讲解了算法实现过程,并提供完整代码示例。

Dynamic len

【题目链接】Dynamic len

【题目类型】带修莫队

&题解:

莫队可以单点更改,只要再多加一维,代表查询次数,排序的时候3个关键字.
之后循环离线的时候,先暴力时间指针(oi大佬说的),之后l,和r就随便了.还有要会用vis数组.

【时间复杂度】\(O(n^{\frac{5}{3}})\)

&代码:

#include <cstdio>
#include <bitset>
#include <iostream>
#include <set>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
using namespace std;
#define INF 0x3f3f3f3f
#define ll long long
const int maxn = 5e4 + 7;
struct nt {
    int l, r, id, ts;
} ask[maxn];
struct nd {
    int x, pre, v;
} p[maxn];
int n, m, an[maxn], c[maxn], pos[maxn], num[maxn * 20], last[maxn], ans;
bool vis[maxn];
char s[9];
bool cmp(nt a, nt b) {
    return pos[a.l] < pos[b.l] ||
           pos[a.l] == pos[b.l] && pos[a.r] < pos[b.r] ||
           pos[a.l] == pos[b.l] && pos[a.r] == pos[b.r] && a.ts < b.ts;
}
void update(int x) {
    if(vis[x]) {
        if(!--num[c[x]]) --ans;
    }
    else if(++num[c[x]] == 1) ans++;
    vis[x] ^= 1;
}
void change(int x, int v) {
    if(vis[x]) {
        update(x); c[x] = v; update(x);
    }
    else c[x] = v;
}
int main() {
    //("E:1.in", "r", stdin);
    scanf("%d%d", &n, &m);
    int x, y, tot = 0, top = 0;
    int bk = sqrt(n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &c[i]);
        last[i] = c[i];
        pos[i] = i / bk;
    }
    for(int i = 1; i <= m; i++) {
        scanf("%s%d%d", s, &x, &y);
        x++;
        if(s[0] == 'Q') {
            //[++tot].id必须要赋为tot. 而且这句话必须在第一位写
            ask[++tot].id = tot;
            ask[tot].l = x, ask[tot].r = y, ask[tot].ts = top;
        }
        else {
            p[++top].x = x, p[top].v = y, p[top].pre = last[x];
            last[x] = y;
        }
    }
    sort(ask + 1, ask + 1 + tot, cmp);
    int now = 0, pl = 1, pr = 0;
    for(int i = 1; i <= tot; i++) {
        if(now < ask[i].ts) {
            for(int j = now + 1; j <= ask[i].ts; j++)
                change(p[j].x, p[j].v);
        }
        else {
            for(int j = now; j >= ask[i].ts + 1; j--)
                change(p[j].x, p[j].pre);
        }
        if(pr < ask[i].r) {
            for(int j = pr + 1; j <= ask[i].r; j++)
                update(j);
        }
        else {
            for(int j = pr; j >= ask[i].r + 1; j--)
                update(j);
        }
        if(pl < ask[i].l) {
            for(int j = pl; j <= ask[i].l - 1; j++)
                update(j);
        }
        else {
            for(int j = pl - 1; j >= ask[i].l; j--)
                update(j);
        }
        pl = ask[i].l, pr = ask[i].r, now = ask[i].ts;
        an[ask[i].id] = ans;
    }
    for(int i = 1; i <= tot; i++) {
        printf("%d\n", an[i]);
    }
    return 0;
}

转载于:https://www.cnblogs.com/s1124yy/p/6817444.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值