zoukankan      html  css  js  c++  java
  • 莫队学习笔记

    普通莫队

    适用范围:离线,区间问询,从 (x) 算出 (x+1,x-1) 的时间复杂度小,以及一切可以用莫队的题目

    不适用范围:在线,奇怪的无法增量式求解的问题,以及一切不可以用莫队的题目

    由于本人过菜,只学不带修不回滚不二次离线的简单莫队。

    假设询问的区间为 ((l,r)),目标函数为 (f(l,r)),那么可以这样做。

    • (l,r) 进行莫队玄学排序
    • 增量式求解(四个 while

    1 玄学排序

    将序列分块。设第 (i) 个数所在块为 (b_i)

    ((l,r)) 按照 (b_l) 为第一关键字,(r) 为第二关键字排序。

    于是我们得到了一个比较规则

    bool cmp(const query &x,const query &y) {
        return b[x.l]==b[y.l] ? x.r<y.r : b[x.l]<b[y.l];
    }
    

    于是我们按照这个比较规则把所有问询区间排序即可。

    2 增量式求解

    接下来就是喜闻乐见的四个 while,用来增量式求解。但是需要注意的是 x++++x 等烦人的细节。

    while(l<q[i].l) del(l++);
    while(l>q[i].l) add(--l);
    while(r<q[i].r) add(++r);
    while(r>q[i].r) del(r--);
    

    deladd 的复杂度为 (t),则莫队的复杂度为 (O(ntsqrt n ))

    例题

    SP3267 D-query

    给定序列 (a_{1...n})(q) 组询问,每组询问为 ([l,r]) 的不同数字的数量。

    莫队板子题。deladd 函数中只需要对各个数字出现次数进行修改即可。

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(register int i=(a);i<=(b);i++)
    using namespace std;
    const int N=200009;
    
    int n,Q,a[N],bs,b[N],ans[N];
    
    struct query{int l,r,id;}q[N];
    bool cmp(const query &x, const query &y) {
    	return b[x.l]==b[y.l] ? x.r<y.r : b[x.l]<b[y.l];
    }
    int l=1,r,cnt[1000009],anss;
    void inc(int x) {cnt[a[x]]++, anss+=(cnt[a[x]]==1);}
    void dec(int x) {cnt[a[x]]--, anss-=(cnt[a[x]]==0);}
    void Mo() {
    	sort(q+1,q+Q+1,cmp);
    	rep(i,1,Q) {
    		while(l<q[i].l) dec(l++);
    		while(l>q[i].l) inc(--l);
    		while(r<q[i].r) inc(++r);
    		while(r>q[i].r) dec(r--);
    		ans[q[i].id]=anss;
    	}
    }
    
    inline int read(){
        register int x=0, f=1; register char c=getchar();
        while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-48,c=getchar();}
        return x*f;
    }
    
    int main() {
    	n=read(); bs=sqrt(n);
    	rep(i,1,n) a[i]=read(), b[i]=(i-1)/bs+1;
    	Q=read();
    	rep(i,1,Q) q[i].l=read(), q[i].r=read(), q[i].id=i;
    	Mo();
    	rep(i,1,Q) printf("%d
    ",ans[i]);
    	return 0;
    }
    

    LG2709 小 B 的询问

    还是一道标准的莫队题。

    (c_i^2) 可以增量式更新,因为 ((c_i+1)^2-c_i^2=2c_i+1)

    #include<bits/stdc++.h>
    #define int long long
    #define rep(i,a,b) for(register int i=(a);i<=(b);i++)
    using namespace std;
    const int N=5e4+9;
    
    int n,m,k,bs,a[N],b[N],ans[N];
    
    struct query {int l,r,id;}q[N];
    bool cmp(const query &x,const query &y) {
    	return b[x.l]==b[y.l] ? x.r<y.r : b[x.l]<b[y.l];
    }
    int l=1,r,anss,cnt[N];
    void inc(int x) {anss+=2*cnt[a[x]]+1, cnt[a[x]]++;}
    void dec(int x) {cnt[a[x]]--, anss-=2*cnt[a[x]]+1;}
    void Mo() {
    	sort(q+1,q+m+1,cmp);
    	rep(i,1,m) {
    		while(l<q[i].l) dec(l++);
    		while(l>q[i].l) inc(--l);
    		while(r<q[i].r) inc(++r);
    		while(r>q[i].r) dec(r--);
    		ans[q[i].id]=anss;
    	}
    }
    
    inline int read(){
        register int x=0, f=1; register char c=getchar();
        while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-48,c=getchar();}
        return x*f;
    }
    
    signed main() {
    	n=read(), m=read(), k=read(), bs=sqrt(n);
    	rep(i,1,n) a[i]=read(), b[i]=(i-1)/bs+1;
    	rep(i,1,m) q[i].l=read(), q[i].r=read(), q[i].id=i;
    	Mo();
    	rep(i,1,m) printf("%lld
    ",ans[i]);
    	return 0;
    }
    

    LG1494 小 Z 的袜子

    简化一下目标函数 (f(l,r))

    [f(l,r)=frac{sum_{i=1}^{n} cnt_i(cnt_i-1) }{(r-l+1)(r-l)} ]

    [=frac{-(r-l+1)+sum_{i=1}^{n} cnt_i^2 }{(r-l+1)(r-l)} ]

    显然分母可以直接求,分子即上题的形式。需要特殊处理一下 (l=r) 的情况。

    #include<bits/stdc++.h>
    #define int long long
    #define rep(i,a,b) for(register int i=(a);i<=(b);i++)
    using namespace std;
    const int N=5e4+9;
    
    int n,m,bs,a[N],b[N];
    pair<int,int> ans[N];
    
    int gcd(int a,int b) {
    	if(b==0) return a;
    	else return gcd(b,a%b);
    } 
    
    struct query {int l,r,id;}q[N];
    bool cmp(const query &x,const query &y) {
    	return b[x.l]==b[y.l] ? x.r<y.r : b[x.l]<b[y.l];
    }
    int l=1,r,anss,cnt[N];
    void inc(int x) {anss+=2*cnt[a[x]]+1, cnt[a[x]]++;}
    void dec(int x) {cnt[a[x]]--, anss-=2*cnt[a[x]]+1;}
    void Mo() {
    	sort(q+1,q+m+1,cmp);
    	rep(i,1,m) {
    		if(q[i].l==q[i].r) {ans[q[i].id]=make_pair(0,1); continue;}
    		while(l<q[i].l) dec(l++);
    		while(l>q[i].l) inc(--l);
    		while(r<q[i].r) inc(++r);
    		while(r>q[i].r) dec(r--);
    		int g=gcd(anss-r+l-1,(r-l+1)*(r-l));
    		ans[q[i].id]=make_pair((anss-r+l-1)/g,(r-l+1)*(r-l)/g);
    	}
    }
    
    inline int read(){
        register int x=0, f=1; register char c=getchar();
        while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-48,c=getchar();}
        return x*f;
    }
    
    signed main() {
    	n=read(), m=read(); bs=sqrt(n);
    	rep(i,1,n) a[i]=read(), b[i]=(i-1)/bs+1;
    	rep(i,1,m) q[i].l=read(), q[i].r=read(), q[i].id=i;
    	Mo();
    	rep(i,1,m) printf("%lld/%lld
    ",ans[i].first,ans[i].second);
    	return 0;
    }
    

    LG4137 Rmq Problem / mex

    暴力更新。复杂度是错的,但是可以艹过很多点,最后还是被最后一个点卡掉了/kk。

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(register int i=(a);i<=(b);i++)
    using namespace std;
    const int N=2e5+9;
    
    int n,m,bs,a[N],b[N],ans[N];
    
    struct query {int l,r,id;}q[N];
    bool cmp(const query &x,const query &y) {
    	return b[x.l]==b[y.l] ? x.r<y.r : b[x.l]<b[y.l];
    }
    int l=1,r,cnt[N],mex;
    inline void dec(int x) {if(a[x]>n+1) return; cnt[a[x]]--, mex=(!cnt[a[x]]&&a[x]<mex ? a[x] : mex);}
    inline void inc(int x) {if(a[x]>n+1) return; cnt[a[x]]++; if(a[x]==mex) while(cnt[mex]) mex++;}
    void Mo() {
    	sort(q+1,q+m+1,cmp);
    	rep(i,1,m) {
    		while(l<q[i].l) dec(l++);
    		while(l>q[i].l) inc(--l);
    		while(r<q[i].r) inc(++r);
    		while(r>q[i].r) dec(r--);
    		ans[q[i].id]=mex;
    	}
    }
    
    
    inline int read(){
        register int x=0, f=1; register char c=getchar();
        while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-48,c=getchar();}
        return x*f;
    }
    
    int main() {
    	n=read(), m=read(), bs=pow(n+2,1./2);
    	rep(i,1,n) a[i]=read(), b[i]=(i-1)/bs+1;
    	rep(i,1,m) q[i].l=read(), q[i].r=read(), q[i].id=i;
    	Mo();
    	rep(i,1,m) printf("%d
    ",ans[i]);
    	return 0;
    }
    

    LG4688 [Ynoi2016]掉进兔子洞

    题目求三个区间不同时出现的数的个数。考虑补集转换。所以目标函数被转换为 (r_1+r_2+r_3-l_1-l_2-l_3+3-3 imes) 三个区间公共的数的个数。

    求交集?用 bitset!对于每一组询问都维护一个 bitset。当然我们不可能每一个询问都暴力加入数。离线区间可增量问询?用莫队!于是我们用莫队来求出每组询问的一个 bitset 即可。

    但是有一个小问题,就是没法开 (10^5) 个 bitset。所以有一个技巧,就是循环利用。什么意思呢?就是我们不是只用一次莫队把所有询问撸完,而是分多次,做多次莫队,这样空间就可以开小一点。

    还有一点需要注意。由于 bitset 不能访问负的下标,所以必须先加后减

    本题卡常。有一个莫队卡常小技巧。排序时,cmp 函数可以这么写,常数就会减少很多。然后就卡到了第二面。

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(register int i=(a);i<=(b);i++)
    using namespace std;
    const int N=1e5+9, M=25009, P=2500;
    
    inline int read(){
        register int x=0, f=1; register char c=getchar();
        while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-48,c=getchar();}
        return x*f;
    }
    
    int n, m, bss, a[N], b[N], ans[M];
    
    int tb[N];
    void discrete() {
    	rep(i,1,n) tb[i]=a[i];
    	sort(tb+1,tb+n+1);
    	rep(i,1,n) a[i]=lower_bound(tb+1,tb+n+1,a[i])-tb;
    } 
    
    struct query {int l, r, id;} q[M*3];
    bool cmp(const query &x, const query &y) {
    	return b[x.l]==b[y.l] ? ((b[x.l]&1) ? x.r<y.r : x.r>y.r) : b[x.l]<b[y.l];
    }
    bitset<N> anss, bs[M];
    int l=1, r, cnt[N];
    bool vst[M];
    inline void inc(int x) {anss[a[x]+(cnt[a[x]]++)]=1;}
    inline void dec(int x) {anss[a[x]+(--cnt[a[x]])]=0;}
    inline void init() {
    	memset(cnt,0,sizeof(cnt));
    	anss.reset();
    	l=1, r=0;
    }
    void work(int m) {
    	init();
    	rep(i,1,m) {
    		q[i*3-2].l=read(), q[i*3-2].r=read(), q[i*3-2].id=i;
    		ans[i]+=q[i*3-2].r-q[i*3-2].l+1;
    		q[i*3-1].l=read(), q[i*3-1].r=read(), q[i*3-1].id=i;
    		ans[i]+=q[i*3-1].r-q[i*3-1].l+1;
    		q[i*3].l=read(), q[i*3].r=read(), q[i*3].id=i;
    		ans[i]+=q[i*3].r-q[i*3].l+1;
    	}
    	sort(q+1,q+m*3+1,cmp);
    	rep(i,1,m*3) {
    		while(l>q[i].l) inc(--l);
    		while(r<q[i].r) inc(++r);
    		while(l<q[i].l) dec(l++);
    		while(r>q[i].r) dec(r--);
    		bs[q[i].id]=(vst[q[i].id] ? bs[q[i].id]&anss : anss);
    		vst[q[i].id]=1;
    	}
    	rep(i,1,m) printf("%d
    ",ans[i]-bs[i].count()*3), ans[i]=0, vst[i]=0;
    }
    
    int main() {
    	n=read(), m=read(), bss=pow(n,0.5);
    	rep(i,1,n) a[i]=read(), b[i]=(i-1)/bss+1;
    	discrete();
    	rep(i,1,m) if(i%P==0||i==m) work(i%P ? m%P : P);
    	return 0;
    }
    
  • 相关阅读:
    .NET面试题目
    ASP.NET应用管理系统框架概述(三)
    ASP.NET应用管理系统框架概述(二)
    ASP.NET应用管理系统框架概述(一)
    InstallShield使用文档
    dotnet的安装包例子
    Access数据库连接池
    InstallShield使用——InstallScript MIS Project例子
    InstallShield使用——例子
    ASP.NET针对Sybase数据库使用分页技术
  • 原文地址:https://www.cnblogs.com/TetrisCandy/p/13775757.html
Copyright © 2011-2022 走看看