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;
    }
    
  • 相关阅读:
    numpy 基础 —— np.linalg
    图像旋转后显示不完全
    opencv ---getRotationMatrix2D函数
    PS1--cannot be loaded because the execution of scripts is disabled on this system
    打开jnlp Faild to validate certificate, the application will not be executed.
    BATCH(BAT批处理命令语法)
    oracle vm virtualbox 如何让虚拟机可以上网
    merge 实现
    Windows batch,echo到文件不成功,只打印出ECHO is on.
    python2.7.6 , setuptools pip install, 报错:UnicodeDecodeError:'ascii' codec can't decode byte
  • 原文地址:https://www.cnblogs.com/lnzwz/p/11291260.html
Copyright © 2011-2022 走看看