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

    莫队学习笔记

    带修莫队

    因为莫队与带修莫队差距不大,放在一块讲。

    莫队是一个离线算法,如果你维护的信息可以(O(1))的进行区间扩展,即从([l,r])([l-1,r],[l,r+1],[l+1,r],[l,r-1])。那么他就可以用莫队算法解决,特殊地,对于修改也可以(O(1))维护的,便可以使用带修莫队。

    具体做法

    将序列分块,每块大小为(B),我们将询问排序,以(l/B)为第一关键字,(r/B)为第二关键字进行排序。对于每个询问,我们暴力移动(l,r),并更新答案。

    我们发现,对于(l)在同一块中的询问,(l)的移动复杂度是(O(B))的,(r)的移动是单调的,复杂度上界为(O(n))

    对于不在同一块之间的移动,我们称为"换块",“换块”一定是相邻两个块之间的,(l)的复杂度是(O(B))的,(r)的复杂度是(O(n))的,

    因为共有((n/B))个块,于是复杂度为(O(nB+n^2/B))(B = sqrt n)时最小。(严谨来讲,如果询问次数为(m),复杂度为(O(mB+n^2/B)),当(B = frac{n}{sqrt m})时最优)。

    对于带修莫队,以修改时间为第三关键字进行排序,类似可以分析得,取(B = n^{frac{2}{3}})时,复杂度最优,为(O(n^{frac{5}{3}}))

    例题

    [国家集训队]数颜色

    模板题,对于修改操作,按照是否在当前区间内分类即可。

    (code:)

    #include<bits/stdc++.h>
    #define ll long long
    #define N 133399
    #define rep(i,a,n) for (int i=a;i<=n;i++)
    #define per(i,a,n) for (int i=n;i>=a;i--)
    #define inf 0x3f3f3f3f
    #define pb push_back
    #define mp make_pair
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define lowbit(i) ((i)&(-i))
    #define VI vector<int>
    #define all(x) x.begin(),x.end()
    #define SZ(x) ((int)x.size())
    using namespace std;
    int B;
    int n,m,a[N],cnt[1000015],ans,res[N];
    VI cur[N];
    struct query{
    	int l,r,t,id;
    	bool operator <(const query &rhs) const{
    		if(l/B != rhs.l/B) return l/B < rhs.l/B;
    		if(r/B != rhs.r/B) return r/B < rhs.r/B;
    		return t < rhs.t;
    	}
    }q[N];
    int top;
    vector<pii> C;
    void add(int x){if(cnt[x]++ == 0) ans++;}
    void del(int x){if(--cnt[x] == 0) ans--;}
    int main(){
    	//freopen(".in","r",stdin);
    	//freopen(".out","w",stdout);
     	scanf("%d%d",&n,&m);
     	C.pb(mp(0,0));
     	B = (int)pow(n,0.666);
     	rep(i,1,n) scanf("%d",&a[i]),cur[i].pb(a[i]);
     	rep(i,1,m){
     		char s[3]; int u,v; scanf("%s%d%d",s+1,&u,&v);
     		if(s[1] == 'Q') q[++top] = (query){u,v,SZ(C)-1,top};
     		else C.pb(mp(u,v));
     	}
     	// puts("Yes");
     	sort(q+1,q+top+1);
     	// cerr << "Yes" << endl;
     	for(int i = 1,l = 1,r = 0,t = 0;i <= top;++i){
     		while(t < q[i].t) { t++;
     			int u = C[t].fi,v = C[t].se;
     			if(l <= u && u <= r) del(a[u]),add(v);
     			a[u] = v; cur[u].pb(v);
     		}
     		while(t > q[i].t){
     			int u = C[t].fi; cur[u].pop_back();
     			int v = cur[u].back();
     			if(l <= u && u <= r) del(a[u]),add(v);
     			a[u] = v; t--;
     		}
     		while(l > q[i].l) add(a[--l]);
     		while(r < q[i].r) add(a[++r]);
     		while(l < q[i].l) del(a[l++]);
     		while(r > q[i].r) del(a[r--]);
     		res[q[i].id] = ans;
     	}
     	rep(i,1,top) printf("%d
    ",res[i]);
    	return 0;
    }
    

    树上莫队

    把树转成欧拉序,在序列上建莫队。设(st[u])为入栈时间戳,(ed[u])为出栈时间戳。

    对于路径((u,v)满足st[u]<st[v])

    1. 如果(u = lca(u,v)),那么路径上的点为欧拉序中(st[u],st[v])之间出现一次的点,其他均出现两次。
    2. 否则,那么路径上的点为欧拉序中(ed[u],st[v])之间出现一次的点,其他均出现两次。

    注意,第2种情况下,两点的(lca)并不会被考虑到,于是要单独加入删除(lca)

    例题

    CodeForces 1479D

    树上莫队维护每个数出现次数的奇偶性,在对值域分块,维护出每块中出现奇数次数的个数即可。

    (code:)

    #include<bits/stdc++.h>
    #define ll long long
    #define N 600015
    #define B 776
    #define rep(i,a,n) for (int i=a;i<=n;i++)
    #define per(i,a,n) for (int i=n;i>=a;i--)
    #define inf 0x3f3f3f3f
    #define pb push_back
    #define mp make_pair
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define lowbit(i) ((i)&(-i))
    #define VI vector<int>
    #define all(x) x.begin(),x.end()
    #define SZ(x) ((int)x.size())
    using namespace std;
    int n,m,a[N],cnt[N],ccnt[N];
    VI e[N];
    int st[N],ed[N],rev[N],clk;
    int fa[N][25],dep[N];
    struct query{
    	int l,r,L,R,lca,id;
    	bool operator <(const query& rhs) const{
    		if(l/B != rhs.l/B) return l/B < rhs.l/B;
    		if((l/B)&1) return r/B < rhs.r/B;
    		return r/B > rhs.r/B;
    	}
    }q[N];
    void dfs(int u,int f){
    	fa[u][0] = f; dep[u] = dep[f]+1; st[u] = ++clk; rev[clk] = u;
    	rep(i,1,19) fa[u][i] = fa[fa[u][i-1]][i-1];
    	for(auto v:e[u]) if(v != f){
    		dfs(v,u);
    	}
    	ed[u] = ++clk; rev[clk] = u;
    }
    int lca(int u,int v){
    	if(dep[u] < dep[v]) swap(u,v);
    	int d = dep[u]-dep[v];
    	rep(i,0,19) if((1<<i)&d) u = fa[u][i];
    	if(u == v) return u;
    	per(i,0,19){
    		if(fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i];
    	}
    	return fa[u][0];
    }
    void getq(){
    	rep(i,1,m){
    		int u,v,l,r; scanf("%d%d%d%d",&u,&v,&l,&r);
    		int LCA = lca(u,v);
    		q[i].id = i; q[i].L = l,q[i].R = r;
    		if(st[u] > st[v]) swap(u,v);
    		if(u == LCA) q[i].l = st[u],q[i].r = st[v];
    		else q[i].l = ed[u],q[i].r = st[v],q[i].lca = LCA;
    	}
    	sort(q+1,q+m+1);
    }
    int ans[N];
    void add(int x){
    	if(cnt[x]) ccnt[x/B]--;
    	else ccnt[x/B]++;
    	cnt[x] ^= 1;
    }
    int getans(int l,int r){
    	rep(i,l/B+1,r/B-1){
    		if(!ccnt[i]) continue;
    		rep(j,i*B,i*B+B-1) if(cnt[j]) return j;
    	}
    	int R = l/B*B+B-1,L = r/B*B;
    	rep(i,l,min(r,R)) if(cnt[i]) return i;
    	rep(i,max(l,L),r) if(cnt[i]) return i;
    	return -1;
    }
    void solve(){
    	for(int i = 1,l = 1,r = 0;i <= m;++i){
    		while(l > q[i].l) add(a[rev[--l]]);
    		while(r < q[i].r) add(a[rev[++r]]);
    		while(l < q[i].l) add(a[rev[l++]]);
    		while(r > q[i].r) add(a[rev[r--]]);
    		if(q[i].lca) add(a[q[i].lca]);
    		ans[q[i].id] = getans(q[i].L,q[i].R);
    		if(q[i].lca) add(a[q[i].lca]);
    	}
    	rep(i,1,m) printf("%d
    ",ans[i]);
    }
    int main(){
    	//freopen(".in","r",stdin);
    	//freopen(".out","w",stdout);
     	scanf("%d%d",&n,&m);
     	rep(i,1,n) scanf("%d",&a[i]);
     	rep(i,2,n){ int u,v; scanf("%d%d",&u,&v);
     		e[u].pb(v); e[v].pb(u);
     	}
     	dfs(1,0);
     	getq();
     	solve();
    	return 0;
    }
    
  • 相关阅读:
    18.9.22 noip模拟赛
    POJ 2299 Ultra-QuickSort
    美团2018年CodeM大赛-初赛B轮 B 配送(最短路)
    Wannafly挑战赛18 E 极差(线段树、单调栈)
    Foj 2299 Prefix(AC自动机、DP)
    求独立矩形个数
    Zoj 3777 Problem Arrangement
    Luogu 2444 [POI2000]病毒 & Zoj 3784
    [ZJOI2007]Hide 捉迷藏
    双联通分量学习笔记
  • 原文地址:https://www.cnblogs.com/czdzx/p/14449900.html
Copyright © 2011-2022 走看看