zoukankan      html  css  js  c++  java
  • 莫队总结 题解

    莫队介绍

    莫队,就是通过离线,将询问排序,然后依次将上一个询问暴力移动到下一个的算法。(通常是区间询问)
    首先,找一种合适的顺序处理,否则显然会T。
    如果只按照左端点排序,那么右端点可能移动次数很多。
    折中一下,将序列以(O(sqrt{n}))分块,按左端点所在块为第一关键字,右端点为第二关键字排序。
    这样,移动次数就是(O(nsqrt{n}))的了。
    证明:左端点:块内每次不超过(O(sqrt{n})),块外总和为(O(n))。右端点:每个块最多从1~n,总共不超过(O(nsqrt{n}))
    优化:
    1.根据经验,数列长n,询问为m时,块大小(B=frac{n}{sqrt{m*frac{2}{3}}})时较优。
    2.我们发现:在第一个块时,右端点从左到右,在第二个块时,右端点还是从左到右,中间需要把右端点移回来,很费时间。
    使用奇偶块优化:

    int cmp(const void*a2,const void*b2)
    {
    	SXw a=*(SXw*)a2,b=*(SXw*)b2;
    	if(ku[a.l]==ku[b.l])
    		return ku[a.l]&1?a.r-b.r:b.r-a.r;
    	return a.l-b.l;
    }
    

    能节省约1/3的时间。

    带修改莫队:

    普通的莫队只能解决不待修改的问题。
    扩展一下也能支持修改。
    普通的莫队有两个指针,加一个指针表示时间即可。
    转移时先考虑时间序。
    注意块大小为(O(n^{2/3})),排序时左端点所在块为第一关键字,右端点所在块为第二关键字,时间序为第三关键字。
    似乎也可以用奇偶块优化。

    代码:

    int cmp(const void*a2,const void*b2)
    {
    	SXw a=*(SXw*)a2,b=*(SXw*)b2;
    	if(ku[a.x]!=ku[b.x])
    		return ku[a.x]-ku[b.x];
    	if(ku[a.y]!=ku[b.y])
    		return ku[a.x]&1?ku[a.y]-ku[b.y]:ku[b.y]-ku[a.y];
    	return ku[a.y]&1?a.t-b.t:b.t-a.t;
    }
    

    树上莫队:

    普通莫队处理序列的操作,但莫队也能处理树上问题。

    树分块:

    (参考课件)
    设块的大小是 B 。
    进行 dfs,搜到某个点时,如果它的前 k 个儿子的剩余节点数之和 >=B 了,
    就把这些点作为一个新的块(注意,不算当前节点),
    存储这些节点可以用一个栈搞。
    扫描完所有点之后,如果还有的点没有被分块,就把这些点连同当前搜到的点一起作为剩余节点返回到上一层。
    搜索结束之后可能还会有点没被分块,把这些点加到最后一个块里就行了。

    指针的转移:

    指针转移没有序列上那样简单。
    (参考课件)
    我们令 S(v,u) 表示 v 到 u 的路径
    那么 S(v,u)=S(u,root) xor S(v,root) xor {lca(u,v)}
    xor 指的是对称差,就相当于去重。
    我们再令 T(u,v)=S(u,root)xor S(v,root)
    当从 T(u,v) 更新到 T(u',v) 时
    我们有:
    T(u',v)=S(root,u') xor S(root,v)
    T(u',v) xor T(u,v)
    =S(root,u') xor S(root,v) xor S(u,root) xor S(v,root)
    =S(root,u') xor S(root,u) =T(u',u).
    lca:每次处理询问的时候更新上,处理完更新回来就行了。
    从 u,v 更新到 u',v':更新两次就好了。
    复杂度也是(O(nsqrt{n}))

    例题

    1、[[HNOI2016]大数]

    [HNOI2016]大数

    使用类似hash的方法提取子串。
    l+1~r的值为(S(r)-S(l)*10^{r-l})
    是7的倍数即(S(r)mod P=S(l)*10^{r-l} mod P)
    两边乘以(10^{n-r}),得(S(r)*10^{n-r} mod P=S(l)*10^{n-l} mod P)
    然后就是区间(S(i)*10^{n-i})出现次数平方和了。

    注意:当P等于2或5时,要特判,考虑个位即可。

    代码:

    #include < stdio.h > #include < string.h > #include < stdlib.h > #include < math.h > #define ll long long char zf[100010];
    int S[100010],
    mi[100010],
    sz[100010];
    struct SPx {
        int z,
        i;
    };
    SPx px[100010];
    int cmp(const void * a, const void * b) {
        return ((SPx * ) a) - >z - ((SPx * ) b) - >z;
    }
    int sl[100010],
    ku[100010];
    ll ans = 0;
    void add(int i) {
        ans = ans + sl[sz[i]];
        sl[sz[i]] += 1;
    }
    void del(int i) {
        ans = ans - sl[sz[i]] + 1;
        sl[sz[i]] -= 1;
    }
    struct SXw {
        int l,
        r,
        i;
    };
    SXw xw[100010];
    int cmp2(const void * a2, const void * b2) {
        SXw a = *(SXw * ) a2,
        b = *(SXw * ) b2;
        if (ku[a.l] == ku[b.l]) return ku[a.l] & 1 ? a.r - b.r: b.r - a.r;
        return a.l - b.l;
    }
    ll jg[100010];
    ll su1[100010],
    su2[100010];
    int main() {
        int p,
        n,
        m,
        k = 0;
        scanf("%d%s%d", &p, zf, &m);
        n = strlen(zf);
        if (p == 2 || p == 5) {
            for (int i = 1; i <= n; i++) {
                su1[i] = su1[i - 1];
                su2[i] = su2[i - 1];
                if ((zf[i - 1] - '0') % p == 0) {
                    su1[i] += i;
                    su2[i] += 1;
                }
            }
            for (int i = 0; i < m; i++) {
                int l,
                r;
                scanf("%d%d", &l, &r);
                printf("%lld
    ", su1[r] - su1[l - 1] - (l - 1) * (su2[r] - su2[l - 1]));
            }
            return 0;
        }
        int si = n / sqrt(m * 2 / 3);
        if (si == 0) si = 1;
        for (int i = 0; i <= n; i++) ku[i] = i / si;
        for (int i = 1; i <= n; i++) S[i] = (S[i - 1] * 10ll + zf[i - 1] - '0') % p;
        mi[0] = 1;
        for (int i = 1; i <= n; i++) mi[i] = mi[i - 1] * 10ll % p;
        for (int i = 0; i <= n; i++) sz[i] = 1ll * S[i] * mi[n - i] % p;
        for (int i = 0; i <= n; i++) {
            px[i].i = i;
            px[i].z = sz[i];
        }
        qsort(px, n + 1, sizeof(SPx), cmp);
        for (int i = 0; i <= n; i++) {
            if (i > 0 && px[i].z != px[i - 1].z) k += 1;
            sz[px[i].i] = k;
        }
        for (int i = 0; i < m; i++) {
            int l,
            r;
            scanf("%d%d", &l, &r);
            xw[i].l = l - 1;
            xw[i].r = r;
            xw[i].i = i;
        }
        qsort(xw, m, sizeof(SXw), cmp2);
        int tl = 0,
        tr = -1;
        for (int i = 0; i < m; i++) {
            int l = xw[i].l,
            r = xw[i].r;
            while (tl - 1 >= l) {
                add(tl - 1);
                tl -= 1;
            }
            while (tr + 1 <= r) {
                add(tr + 1);
                tr += 1;
            }
            while (tl + 1 <= l) {
                del(tl);
                tl += 1;
            }
            while (tr - 1 >= r) {
                del(tr);
                tr -= 1;
            }
            jg[xw[i].i] = ans;
        }
        for (int i = 0; i < m; i++) printf("%lld
    ", jg[i]);
        return 0;
    }
    

    2、 Gty的二逼妹子序列

    Gty的二逼妹子序列
    CDQ被卡空间……
    考虑莫队:类似HH的项链,直接维护单点修改,区间求和。
    需要(O(nsqrt{n}))次修改,然而只有(O(m))次询问。
    如果树状数组的话十分浪费。
    考虑均衡下复杂度:使用一种修改快,询问慢的方法维护。
    分块即可。(O(1))修改,(O(sqrt{n}))查询。
    注意空间。

    代码:

    #include < stdio.h > 
    #include < math.h > 
    #include < algorithm > 
    using namespace std;
    int sz[100002],sl[100002],si;
    struct SXw {
    	int l,r,a,b,i;
    	SXw() {}
    	SXw(int L, int R, int A, int B, int I) {
    		l = L;	r = R;	a = A;	b = B;	i = I;
    	}
    };
    SXw xw[1000002];
    bool operator < (SXw a, SXw b) {
    	if (a.l / si == b.l / si) return (a.l / si) & 1 ? a.r < b.r: b.r < a.r;
    	return a.l < b.l;
    }
    int st[1002],en[1002],he[1002],wz[100002],dx,ks;
    int ans[1000002];
    void yucl(int n) {
    	dx = int(pow(n, 0.47) + 0.5);
    	while (1) {
    		st[ks] = (ks == 0 ? 1 : en[ks - 1] + 1);
    		en[ks] = st[ks] + dx - 1;
    		if (en[ks] > n) en[ks] = n;
    		ks += 1;
    		if (en[ks - 1] == n) break;
    	}
    	for (int i = 0; i < ks; i++) {
    		for (int j = st[i]; j <= en[i]; j++) wz[j] = i;
    	}
    }
    void addk(int i, int x) {
    	he[wz[i]] += x;
    }
    int blsum(int l, int r) {
    	int rt = 0;
    	for (int i = l; i <= r; i++) rt += (sl[i] > 0);
    	return rt;
    }
    int sum(int l, int r) {
    	if (wz[l] == wz[r]) return blsum(l, r);
    	int x = wz[l] + 1,
    	y = wz[r] - 1,
    	rt = 0;
    	for (int i = x; i <= y; i++) rt += he[i];
    	rt += blsum(l, st[x] - 1) + blsum(en[y] + 1, r);
    	return rt;
    }
    void add(int x) {
    	sl[x] += 1;
    	if (sl[x] == 1) addk(x, 1);
    }
    void del(int x) {
    	sl[x] -= 1;
    	if (sl[x] == 0) addk(x, -1);
    }
    int main() {
    	int n,m;
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; i++) scanf("%d", &sz[i]);
    	si = n / sqrt(m * 2 / 3);
    	if (si == 0) si = 1;
    	for (int i = 0; i < m; i++) {
    		int l,r,a,b;
    		scanf("%d%d%d%d", &l, &r, &a, &b);
    		xw[i] = SXw(l, r, a, b, i);
    	}
    	sort(xw, xw + m);
    	yucl(n);
    	int tl = 1,tr = 0;
    	for (int i = 0; i < m; i++) {
    		int l = xw[i].l,r = xw[i].r;
    		while (tl - 1 >= l) {
    			tl -= 1;
    			add(sz[tl]);
    		}
    		while (tr + 1 <= r) {
    			tr += 1;
    			add(sz[tr]);
    		}
    		while (tl + 1 <= l) {
    			del(sz[tl]);
    			tl += 1;
    		}
    		while (tr - 1 >= r) {
    			del(sz[tr]);
    			tr -= 1;
    		}
    		ans[xw[i].i] = sum(xw[i].a, xw[i].b);
    	}
    	for (int i = 0; i < m; i++) printf("%d
    ", ans[i]);
    	return 0;
    }
    

    3、[WC2013]糖果公园

    [WC2013]糖果公园

    就是带修改树上莫队。
    注意卡常。

    代码:

    #include < stdio.h > 
    #include < math.h > 
    #include < stdlib.h > 
    #define ll long long
    #define re register int fr[100010],ne[200010],v[200010],bs = 0;
    void addb(int a, int b) {
    	v[bs] = b;
    	ne[bs] = fr[a];
    	fr[a] = bs++;
    }
    int st[100010],tp = 0,B,ku[100010],ks = 0;
    void dfs0(int u, int f) {
    	for (int i = fr[u]; i != -1; i = ne[i]) {
    		if (v[i] == f) continue;
    		dfs0(v[i], u);
    		if (tp >= B) {
    			for (int j = 0; j < tp; j++) ku[st[j]] = ks;
    			ks += 1;
    			tp = 0;
    		}
    	}
    	st[tp++] = u;
    }
    int fa[100010],son[100010],top[100010],sd[100010];
    int dfs1(int u, int f) {
    	int he = 1,ma = -1;
    	son[u] = -1;sd[u] = sd[f] + 1;
    	fa[u] = f;
    	for (int i = fr[u]; i != -1; i = ne[i]) {
    		if (v[i] == f) continue;
    		int rt = dfs1(v[i], u);
    		he += rt;
    		if (rt > ma) {
    			ma = rt;
    			son[u] = v[i];
    		}
    	}
    	return he;
    }
    void dfs2(int u, int f, int tp) {
    	top[u] = tp;
    	if (son[u] == -1) return;
    	dfs2(son[u], u, tp);
    	for (int i = fr[u]; i != -1; i = ne[i]) {
    		if (v[i] != f && v[i] != son[u]) dfs2(v[i], u, v[i]);
    	}
    }
    int getlca(int x, int y) {
    	while (top[x] != top[y]) {
    		if (sd[top[x]] > sd[top[y]]) x = fa[top[x]];
    		else y = fa[top[y]];
    	}
    	if (sd[x] < sd[y]) return x;
    	else return y;
    }
    int sl[100010],co[100010];
    ll he = 0,ans[100010];
    int V[100010],W[100010];
    bool bk[100010];
    void add(re int x) {
    	sl[x] += 1;
    	he += 1ll * W[sl[x]] * V[x];
    }
    void del(re int x) {
    	he -= 1ll * W[sl[x]] * V[x];
    	sl[x] -= 1;
    }
    void xiug(re int x, re int y) {
    	if (bk[x]) {
    		del(co[x]);
    		add(y);
    	}
    	co[x] = y;
    }
    void Xord(re int x) {
    	if (bk[x]) del(co[x]);
    	else add(co[x]);
    	bk[x] = !bk[x];
    }
    void Xor(re int x, re int y) {
    	re int lc = getlca(x, y);
    	while (x != lc) {
    		Xord(x);
    		x = fa[x];
    	}
    	while (y != lc) {
    		Xord(y);
    		y = fa[y];
    	}
    }
    void move(re int a, re int b, re int x, re int y) {
    	re int lc1 = getlca(a, b);
    	re int lc2 = getlca(x, y);
    	Xord(lc1);
    	Xor(a, x);
    	Xor(b, y);
    	Xord(lc2);
    }
    struct SXw {
    	int x,y,i,t;
    	SXw() {}
    	SXw(int X, int Y, int I, int T) {
    		x = X;y = Y;
    		i = I;t = T;
    	}
    };
    SXw xw[100010];
    int cmp(const void * a2, const void * b2) {
    	SXw a = *(SXw * ) a2,
    	b = *(SXw * ) b2;
    	if (ku[a.x] != ku[b.x]) return ku[a.x] - ku[b.x];
    	if (ku[a.y] != ku[b.y]) return ku[a.x] & 1 ? ku[a.y] - ku[b.y] : ku[b.y] - ku[a.y];
    	return ku[a.y] & 1 ? a.t - b.t: b.t - a.t;
    }
    int wz[100010],ol[100010],xg[100010],tm[100010];
    int main() {
    	int n,m,q,
    	k = 0,s = 0;
    	scanf("%d%d%d", &n, &m, &q);
    	for (int i = 1; i <= m; i++) scanf("%d", &V[i]);
    	for (int i = 1; i <= n; i++) scanf("%d", &W[i]);
    	for (int i = 1; i <= n; i++) fr[i] = -1;
    	for (int i = 0; i < n - 1; i++) {
    		int a,b;
    		scanf("%d%d", &a, &b);
    		addb(a, b);
    		addb(b, a);
    	}
    	for (int i = 1; i <= n; i++) {
    		scanf("%d", &co[i]);
    		tm[i] = co[i];
    	}
    	B = int(pow(n, 0.62) + 0.5);
    	dfs0(1, 0);dfs1(1, 0),
    	dfs2(1, 0, 1);
    	for (int i = 0; i < tp; i++) ku[st[i]] = ks;
    	ks += 1;
    	for (int i = 0; i < q; i++) {
    		int ty,x,y;
    		scanf("%d%d%d", &ty, &x, &y);
    		if (ty == 0) {
    			k += 1;
    			wz[k] = x;
    			ol[k] = tm[x];
    			xg[k] = tm[x] = y;
    		} else {
    			int t = s;
    			xw[s++] = SXw(x, y, t, k);
    		}
    	}
    	qsort(xw, s, sizeof(SXw), cmp);
    	re int lx = 1,ly = 1,lt = 0;	
            Xord(1);
    	for (int i = 0; i < s; i++) {
    		while (lt + 1 <= xw[i].t) {
    			lt += 1;
    			xiug(wz[lt], xg[lt]);
    		}
    		while (lt - 1 >= xw[i].t) {
    			xiug(wz[lt], ol[lt]);
    			lt -= 1;
    		}
    		move(lx, ly, xw[i].x, xw[i].y);
    		lx = xw[i].x;
    		ly = xw[i].y;
    		ans[xw[i].i] = he;
    	}
    	for (int i = 0; i < s; i++) printf("%lld
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Django配置MySQL数据库教程
    给大家发个Python和Django的福利吧,不要钱的那种~~~
    Django 设置admin后台表和App(应用)为中文名
    Django 配置JWT认证方式
    威联通(NAS)应用篇:自建OwnCloud网盘(百度网盘,拜拜~~~)
    请不要浪费你的生命,一文多发推广就用它(OpenWrite)
    Django ORM 查询表中某列字段值
    【Django 2.2文档系列】Model 外键中的on_delete参数用法
    嵌入式、C语言位操作的一些技巧汇总
    【RT-Thread笔记】IO设备模型及GPIO设备
  • 原文地址:https://www.cnblogs.com/lnzwz/p/11291260.html
Copyright © 2011-2022 走看看