莫队算法

莫队

普通莫队

注意顺序正确!!

codes
        for (int i = 1; i <= m; i++) {
            int pl = q[i].l, pr = q[i].r;
            while (l > pl) add(a[--l]);
            while (r < pr) add(a[++r]);
            while (l < pl) del(a[l++]);
            while (r > pr) del(a[r--]);
            ans[q[i].id] = cal(q[i].R) - cal(q[i].L - 1);
        }

值域二分

https://acm.hdu.edu.cn/showproblem.php?pid=6959

\(O(1)\)修改,\(O(sqrt(m))\)查询,在n,m等阶的情况下 ,复杂度平均只有\(O(n*sqrt(n)+ m*sqrt(n))\)比线段树等数据结构维护O(long(n))地修改查询优.

upd: 多组输入注意memset..

codes
const int maxn = 1e5 + 7;

int n, m, t, k = sqrt(maxn), a[maxn], num[maxn], sum[maxn], ans[maxn];

struct query {
    int l, r, L, R, id;
} q[maxn];

bool cmp(query a, query b) {
    if (a.l / k != b.l / k)
        return a.l / k < b.l / k;
    if ((a.l / k) & 1)
        return a.r < b.r;
    else
        return a.r > b.r;
}

void add(int x) {
    num[x]++;
    if (num[x] == 1)
        sum[x / k]++;
}

void del(int x) {
    num[x]--;
    if (num[x] == 0)
        sum[x / k]--;
}

int cal(int x) {
    int res = 0;
    for (int i = 0; i < x / k; i++)
        res += sum[i];
    for (int i = (x / k) * k; i <= x; i++)
        res += (num[i] >= 1);
    return res;
}

void solve() {
    cin >> t;
    while (t--) {
        memset(num, 0, sizeof num);
        memset(sum, 0, sizeof sum);
        cin >> n >> m;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        for (int i = 1; i <= m; i++)
            cin >> q[i].l >> q[i].L >> q[i].r >> q[i].R, q[i].id = i;
        sort(q + 1, q + 1 + m, cmp);
        int l = 1, r = 0;
        for (int i = 1; i <= m; i++) {
            int pl = q[i].l, pr = q[i].r;
            while (l > pl) add(a[--l]);
            while (r < pr) add(a[++r]);
            while (l < pl) del(a[l++]);
            while (r > pr) del(a[r--]);
            ans[q[i].id] = cal(q[i].R) - cal(q[i].L - 1);
        }
        for (int i = 1; i <= m; i++)
            cout << ans[i] << "\n";
    }
}

带修莫队

https://www.luogu.com.cn/problem/P1903

  • 修改记录时间,修改值,修改后值 同时读入修改时进行修改,修改结束后还原

  • 分块大小:\(pow(n,2.0/3)\)

codes

const int maxn = 2e6 + 7;

int n, t, qn, m, k, a[maxn], ans[maxn], num[maxn], sum;
char op;

struct query {
    int l, r, t, id;
} q[maxn];

struct change {
    int pl, val, oval;
} chg[maxn];

bool cmp(query a, query b) {
    if (a.l / k != b.l / k) return a.l / k < b.l / k;
    if (a.r / k != b.r / k) return a.r / k < b.r / k;
    return a.t < b.t;
}

void add(int x) {
    if (x == 0)
        return;
    num[x]++;
    sum += (num[x] == 1);
}


void del(int x) {
    if (x == 0)
        return;
    num[x]--;
    sum -= (num[x] == 0);
}

int l = 1, r = 0, tn = 0;

void addchange(int t) {
    int pl = chg[t].pl, val = chg[t].val;
    if (pl >= l && pl <= r)
        add(val), del(a[pl]);
    a[pl] = val;
}

void delchange(int t) {
    int pl = chg[t].pl, val = chg[t].oval;
    if (pl >= l && pl <= r)
        add(val), del(a[pl]);
    a[pl] = val;
}

void solve() {
    cin >> n >> m;
    k = pow(n, 2.0 / 3);
    for (int i = 1; i <= n; i++) cin >> a[i];

    for (int i = 1, l, r; i <= m; i++) {
        cin >> op >> l >> r;
        if (op == 'R') chg[++t].pl = l, chg[t].val = r, chg[t].oval = a[l], a[l] = r;
        else q[++qn].l = l, q[qn].r = r, q[qn].t = t, q[qn].id = qn;
    }

    for (int i = t; i >= 1; i--) a[chg[i].pl] = chg[i].oval;

    sort(q + 1, q + 1 + qn, cmp);

    for (int i = 1; i <= qn; i++) {
        int L = q[i].l, R = q[i].r, T = q[i].t;

        while (tn < T) addchange(++tn);
        while (tn > T) delchange(tn--);
        while (l > L) add(a[--l]);
        while (r < R) add(a[++r]);
        while (l < L) del(a[l++]);
        while (r > R) del(a[r--]);

        ans[q[i].id] = sum;
    }
    for (int i = 1; i <= qn; i++)
        cout << ans[i] << "\n";
}

https://codeforces.com/contest/1234/problem/D

  • 修改添加和删除的代码很重要..
  • 可以树状数组写..
codes
const int maxn = 1e5 + 7;

int n, t, m, k, l, r;

char s[maxn], ch;
int ans[maxn], sum, num[200];


int qn;
struct query {
    int l, r, id, t;
} q[maxn];

int tn;
struct change {
    int pl;
    char val, oval;
} c[maxn];

bool cmp(query a, query b) {
    if (a.l / k != b.l / k) return a.l / k < b.l / k;
    if (a.r / k != b.r / k) return a.r / k < b.r / k;
    return a.t < b.t;
}


void add(char ch) {
    num[ch]++;
    if (num[ch] == 1)
        sum++;
}

void del(char ch) {
    num[ch]--;
    if (num[ch] == 0)
        sum--;
}

void addchange(int t) {
    int pl = c[t].pl, val = c[t].val;
    if (pl >= l && pl <= r)
        add(val), del(s[pl]);
    s[pl] = val;
}

void delchange(int t) {
    int pl = c[t].pl, val = c[t].oval;
    if (pl >= l && pl <= r)
        add(val), del(s[pl]);
    s[pl] = val;
}

void solve() {
    cin >> (s + 1) >> m;
    k = pow(strlen(s + 1), 2.0 / 3);
    for (int i = 1; i <= m; i++) {
        cin >> n;
        if (n == 1) {
            cin >> n >> ch;
            c[++tn].pl = n, c[tn].val = ch, c[tn].oval = s[n];
            s[n] = ch;
        } else {
            cin >> l >> r;
            qn++, q[qn] = {l, r, qn, tn};
        }
    }

    for (int i = tn; i >= 1; i--)
        s[c[i].pl] = c[i].oval;

    sort(q + 1, q + 1 + qn, cmp);

    l = 1, r = 0, t = 0;

    for (int i = 1; i <= qn; i++) {
        int pl = q[i].l, pr = q[i].r, pt = q[i].t;
        while (t < pt) addchange(++t);
        while (t > pt) delchange(t--);
        while (l > pl) add(s[--l]);
        while (r < pr) add(s[++r]);
        while (l < pl) del(s[l++]);
        while (r > pr) del(s[r--]);
        ans[q[i].id] = sum;
    }
    for (int i = 1; i <= qn; i++)
        cout << ans[i] << endl;
}

树上莫队

伪树上莫队

树剖下加,对每次询问拆出来就行了..

codes

真树上莫队

codes

回滚莫队

莫队配合bitset

https://www.luogu.com.cn/problem/P4688

  • 离散化时lower_bound可以记录下上一个数到多少位
  • 用bitset维护相同的数的个数时,如果个数最大是固定的,可以记录一个cnt[]数组,来记录当前数用到了第多少位
  • 空间开不下时,可以对询问分块处理
codes
const int maxn = 1e5 + 7;

int n, t, m;
int a[maxn], b[maxn];

int cnt = 0;
set<int> st;
map<int, int> mp;

int k;
struct query {
    int id, l, r;
} q[maxn];

bool cmp(query a, query b) {
    if (a.l / k == b.l / k)
        return a.r < b.r;
    return a.l < b.l;
}

bitset<maxn> bst[maxn / 3];
bitset<maxn> now;

int ans[maxn], pl[maxn];

void add(int x) {
    now.set(x + pl[x]);
    pl[x]++;
}

void del(int x) {
    pl[x]--;
    now.reset(x + pl[x]);
}

void solve(int sl, int sr) {
    for (int i = 0; i <= sr - sl + 1; i++)
        bst[i].set();
    memset(pl, 0, sizeof pl);
    now.reset();
    int tot = 0;
    for (int i = sl, l, r; i <= sr; i++) {
        for (int j = 1; j <= 3; j++) {
            cin >> l >> r;
            ans[i] += (r - l + 1);
            q[tot++] = {i - sl + 1, l, r};
        }
    }
    sort(q, q + tot, cmp);
    int l = 1, r = 0;
    for (int i = 0; i < tot; i++) {
        int pl = q[i].l, pr = q[i].r;
        while (l > pl) add(a[--l]);
        while (r < pr) add(a[++r]);
        while (l < pl) del(a[l++]);
        while (r > pr) del(a[r--]);
        bst[q[i].id] &= now;
    }
    for (int i = sl; i <= sr; i++)
        ans[i] -= bst[i - sl + 1].count() * 3;
}

void solve() {
    cin >> n >> m;
    k = sqrt(n) + 1;
    for (int i = 1; i <= n; i++)
        cin >> a[i], b[i] = a[i];
    sort(b + 1, b + 1 + n);
    for (int i = 1; i <= n; i++)
        a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b;

    solve(1, m / 3);
    solve(m / 3 + 1, m / 3 * 2);
    solve(m / 3 * 2 + 1, m);

    for (int i = 1; i <= m; i++)
        cout << ans[i] << "\n";
}

https://www.luogu.com.cn/problem/P3674

  • a - b = x 可以转换成 a = b + x.用bitset记录区间数的种类,存在a - b = x 等价于 bitset & bitset<<x 是否有1
  • a + b = x 可以记b = N - B,也就是判断 a - B = x - N是否存在,另外记录一个bitset就能转化到两数之差
  • a * b = x 直接枚举因子即可
codes
const int maxn = 1e5 + 7;

int n, t, m, k;
int a[maxn], ans[maxn];

struct query {
    int id, op, x, l, r;
} q[maxn];

bool cmp(query a, query b) {
    if (a.l / k == b.l / k)
        return a.r < b.r;
    return a.l / k < b.l / k;
}

bitset<maxn> now, eth;
int cnt[maxn], nct[maxn];

void add(int x) {
    cnt[x]++, nct[maxn - x]++;
    now.set(x);
    eth.set(maxn - x);
}

void del(int x) {
    cnt[x]--, nct[maxn - x]--;
    if (cnt[x] == 0)
        now.reset(x);
    if (nct[maxn - x] == 0)
        eth.reset(maxn - x);
}

void solve() {
    cin >> n >> m;
    k = (int) sqrt(maxn);
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= m; i++) {
        cin >> q[i].op >> q[i].l >> q[i].r >> q[i].x;
        q[i].id = i;
    }

    sort(q + 1, q + 1 + m, cmp);

    int l = 1, r = 0;

    for (int i = 1; i <= m; i++) {
        int pl = q[i].l, pr = q[i].r;
        while (l > pl) add(a[--l]);
        while (r < pr) add(a[++r]);
        while (l < pl) del(a[l++]);
        while (r > pr) del(a[r--]);
        if (q[i].op == 1) {
            ans[q[i].id] = (int) (now & (now << q[i].x)).any();
        } else if (q[i].op == 2) {
            ans[q[i].id] = (int) (now & (eth >> (maxn - q[i].x))).any();
        } else {
            for (int c = 1; c * c <= q[i].x; c++) {
                if (q[i].x % c) continue;
                if (now[c] && now[q[i].x / c])
                    ans[q[i].id]++;
            }
        }
    }
    for (int i = 1; i <= m; i++)
        if (ans[i]) cout << "hana" << endl;
        else cout << "bi" << endl;
}

https://www.luogu.com.cn/problem/P5355

多了个除法的处理,对于小于k的,维护下以每个点为右端点时,左端点最右能到达的地方

codes
const int maxn = 1e5 + 7;
const int k = 310;
int n, t, m;
int a[maxn], ans[maxn];

struct query {
    int id, op, x, l, r;
} q[maxn];

bool cmp(query a, query b) {
    if (a.l / k == b.l / k)
        return a.r < b.r;
    return a.l / k < b.l / k;
}

bitset<maxn> now, eth;
int cnt[maxn], nct[maxn];

void add(int x) {
    cnt[x]++, nct[maxn - x]++;
    now.set(x);
    eth.set(maxn - x);
}

void del(int x) {
    cnt[x]--, nct[maxn - x]--;
    if (cnt[x] == 0)
        now.reset(x);
    if (nct[maxn - x] == 0)
        eth.reset(maxn - x);
}

int mxl[k][maxn], lst[maxn];

void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    for (int x = 1; x < k; x++) {
        memset(lst, 0, sizeof lst);
        for (int i = 1; i <= n; i++) {
            lst[a[i]] = i;
            mxl[x][i] = mxl[x][i - 1];
            if (a[i] % x == 0 && lst[a[i] / x])
                mxl[x][i] = max(mxl[x][i], lst[a[i] / x]);
            if (a[i] * x < maxn && lst[a[i] * x])
                mxl[x][i] = max(mxl[x][i], lst[a[i] * x]);
        }
    }

    for (int i = 1; i <= m; i++) {
        cin >> q[i].op >> q[i].l >> q[i].r >> q[i].x;
        q[i].id = i;
    }

    sort(q + 1, q + 1 + m, cmp);

    int l = 1, r = 0;

    for (int i = 1; i <= m; i++) {
        int pl = q[i].l, pr = q[i].r;
        while (l > pl) add(a[--l]);
        while (r < pr) add(a[++r]);
        while (l < pl) del(a[l++]);
        while (r > pr) del(a[r--]);
        if (q[i].op == 1) {
            ans[q[i].id] = (int) (now & (now << q[i].x)).any();
        } else if (q[i].op == 2) {
            ans[q[i].id] = (int) (now & (eth >> (maxn - q[i].x))).any();
        } else if (q[i].op == 3) {
            for (int c = 1; c * c <= q[i].x; c++) {
                if (q[i].x % c) continue;
                if (now[c] && now[q[i].x / c])
                    ans[q[i].id]++;
            }
        } else if (q[i].op == 4) {
            if (q[i].x >= k) {
                for (int c = 1; c * q[i].x < maxn; c++)
                    if (now[c] && now[c * q[i].x]) {
                        ans[q[i].id]++;
                        break;
                    }
            } else {
                if (q[i].x == 0) continue;
                if (mxl[q[i].x][q[i].r] >= q[i].l)
                    ans[q[i].id]++;
            }
        }
    }
    for (int i = 1; i <= m; i++)
        if (ans[i]) cout << "yuno" << endl;
        else cout << "yumi" << endl;
}
posted @ 2021-08-09 20:13  naymi  阅读(26)  评论(0)    收藏  举报