zoukankan      html  css  js  c++  java
  • 莫队

    莫队

    • 解决区间统计问题 如这题

    • 分块 + 排序 + 加减操作移动 + 统计

    • 复杂度: (O(Nsqrt{N}))

    • 思想:通过分块和排序后,减少相邻区间的移动操作次数,并在区间移动过程中进行区间中统计

    • 本质:二维扫描线,在二维平面上求曼哈顿距离最小生成树

    • 带修莫队即加入时间一维,变为三维扫描线

    • 树上莫队可以用括号序转为序列上的操作

    例题 :

    P3901

    SP3267

    [国家集训队]小Z的袜子 /【模板】莫队

    [国家集训队]数颜色 / 维护队列 /【模板】带修莫队

    【模板】回滚莫队&不删除莫队

    [WC2013]糖果公园

    各种莫队的模板

    //普通莫队
    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    
    ll read() {
      ll a = 0, f = 1; char c = getchar();
      while(c > '9' || c < '0') {if(c == '-') f = -1; c = getchar();}
      while(c >= '0' && c <= '9') {a = a * 10 + c - '0'; c = getchar();}
      return a * f;
    }
    
    const int MAXN = 5e5 + 14;
    
    ll n, m, xl, xr, block;
    ll ans;
    ll a[MAXN];
    ll cnt[MAXN];
    ll s[MAXN], w[MAXN];
    struct question {
      int l, r, bo, id;
      #define l(x) ask[x].l
      #define r(x) ask[x].r
      #define bo(x) ask[x].bo
      #define id(x) ask[x].id
    } ask[MAXN];
    
    bool cmp(question a, question b) {
      if(a.bo == b.bo) return a.r < b.r;
      else return a.bo < b.bo;
    }
    void add(int x) {
      if(cnt[a[x]] > 0) ans += cnt[a[x]];
      cnt[a[x]]++;
    }
    void del(int x) {
      cnt[a[x]]--;
      if(cnt[a[x]] > 0) ans -= cnt[a[x]];
    }
    ll gcd(ll x, ll y) {
      if(y == 0) return x;
      else return gcd(y, x % y);
    }
    
    int main() {
      
      n = read(), m = read(); block = sqrt(n);
      for (int i = 1; i <= n; i++) a[i] = read();
      for (int i = 1; i <= m; i++) l(i) = read(), r(i) = read(), bo(i) = l(i) / block, id(i) = i;
    
      sort(ask + 1, ask + m + 1, cmp);
    
      for (int i = 1; i <= m; i++) {
        while(xr > r(i)) del(xr--);
        while(xr < r(i)) add(++xr);
        while(xl < l(i)) del(xl++);
        while(xl > l(i)) add(--xl);
        if(xl == xr) {
          s[id(i)] = 0; w[id(i)] = 1;continue;
        } 
        s[id(i)] = ans; w[id(i)] = (xr - xl + 1) * (xr - xl) / 2;
        if(s[id(i)] == 0) w[id(i)] = 1;
        else {
          ll x = gcd(s[id(i)], w[id(i)]);
          s[id(i)] /= x; w[id(i)] /= x;
        }
      }
      
      for (int i = 1; i <= m; i++) {  
        printf("%lld/%lld
    ", s[i], w[i]);
      }
    
      return 0;
    }
    
    //带修
    //块大小为n^(2/3),复杂度O(n^(3/4))
    const int MAXN = 2000010;
    int n, m, qu, xt;
    int block, B[MAXN];
    int nl = 1, nr = 0, now = 0, a[MAXN], cnt[MAXN], tmp;
    int c[MAXN][2];//0表示位置,1表示值
    int ans[MAXN];
    struct question
    {
    	int l, r, ti, id;
    }ask[MAXN];
    inline bool cmp(const question &a, const question &b)
    {
    	return B[a.l] ^ B[b.l] ? B[a.l] < B[b.l] : B[a.r] ^ B[b.r] ? B[a.r] < B[b.r] : a.ti < b.ti;
    }
    inline void add(int x)
    {
    	if(!cnt[a[x]]) tmp++;
    	cnt[a[x]]++;
    }
    inline void del(int x)
    {
    	cnt[a[x]]--;
    	if(!cnt[a[x]]) tmp--;
    }
    int main()
    {
    	scanf("%d %d", &n, &m);
    	block = pow(n, 2.0 / 3.0);
    	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    	for (int i = 1; i <= n; i++) B[i] = (i - 1) / block + 1;
    	for (int i = 1; i <= m; i++)
    	{
    		char opt[3];
    		scanf("%s", opt + 1);
    		if(opt[1] == 'Q')
    		{
    			++qu;
    			scanf("%d %d", &ask[qu].l, &ask[qu].r);
    			ask[qu].id = qu; ask[qu].ti = xt;
    		}
    		else 
    		{
    			++xt;
    			scanf("%d %d", &c[xt][0], &c[xt][1]);
    		}
    	}
    	sort(ask + 1, ask + qu + 1, cmp);
    	for (int i = 1; i <= qu; i++)
    	{
    		while(nl > ask[i].l) add(--nl);
    		while(nl < ask[i].l) del(nl++);
    		while(nr > ask[i].r) del(nr--);
    		while(nr < ask[i].r) add(++nr);
    		while(ask[i].ti > now)//往前更新
    		{
    			++now;
    			if(c[now][0] >= nl && c[now][0] <= nr)
    			{
    				cnt[ a[c[now][0]] ]--;
    				if(!cnt[ a[c[now][0]] ]) --tmp;
    			}
    			swap(a[ c[now][0] ], c[now][1]);
    			if(c[now][0] >= nl && c[now][0] <= nr)
    			{
    				if(!cnt[ a[c[now][0]] ]) ++tmp;
    				cnt[ a[c[now][0]] ]++;
    			}
    
    		}
    		while(ask[i].ti < now)//往回撤销
    		{
    			if(c[now][0] >= nl && c[now][0] <= nr)
    			{
    				cnt[ a[c[now][0]] ]--;
    				if(!cnt[ a[c[now][0]] ]) --tmp;
    			}
    			swap(a[ c[now][0] ], c[now][1]);
    			if(c[now][0] >= nl && c[now][0] <= nr)
    			{
    				if(!cnt[ a[c[now][0]] ]) ++tmp;
    				cnt[ a[c[now][0]] ]++;
    			}
    			--now;
    		}
    		ans[ ask[i].id ] = tmp;
    	}
    	for (int i = 1; i <= qu; i++)
    	{
    		printf("%d
    ", ans[i]);
    	}
    	return 0;
    }
    
    //树上
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 1000010;
    const int LOG = 20;
    int n, m, q, nl = 1, nr = 0, nt = 0;
    long long vm[MAXN], wn[MAXN], a[MAXN], c[MAXN][2];
    int block, B[MAXN];
    int head[MAXN], nxt[MAXN << 1], v[MAXN << 1], cnt;
    int qu, tx;
    int dfn[MAXN], ind, fi[MAXN], se[MAXN];
    int anc[MAXN][LOG], depth[MAXN];
    int vis[MAXN];
    int ct[MAXN];
    long long res[MAXN], ans;
    struct question
    {
    	int l, r, ti, id;
    } ask[MAXN];
    inline bool cmp(const question &a, const question &b)
    {
    	if(B[fi[a.l]] ^ B[fi[b.l]]) return B[fi[a.l]] < B[fi[b.l]];
    	if(B[fi[a.r]] ^ B[fi[b.r]]) return B[fi[a.r]] < B[fi[b.r]];
    	return a.ti < b.ti;
    }
    inline void add(int x, int y)
    {
    	nxt[++cnt] = head[x]; head[x] = cnt; v[cnt] = y;
    	nxt[++cnt] = head[y]; head[y] = cnt; v[cnt] = x;
    }
    inline void dfsl(int u, int p, int d) 
    {
        anc[u][0] = p; depth[u] = d;
        for (int i = head[u]; i; i = nxt[i]) 
        {
            if(v[i] == p) continue;
            dfsl(v[i], u, d + 1);
        }
    }
    inline void init() {
    	dfsl(1, 0, 1);
    	for (int j = 1; j < LOG; j++) 
    		for (int i = 1; i <= n; i++) 
    			anc[i][j] = anc[ anc[i][j - 1] ][j - 1];
    }
    inline void swim(int &x, int h) {
    	for (int i = 0; h > 0; i++) 
    	{
    		if(h & 1) x = anc[x][i];
    		h >>= 1;
    	}
    }
    inline int LCA(int x, int y) 
    {
    	if(depth[x] < depth[y]) swap(x, y);
    	if(y == 1) return 1;
    	swim(x, depth[x] - depth[y]);
    	if(x == y) return x;
    	for (int i = LOG - 1; i >= 0; i--) 
    	{
    		if(anc[x][i] != anc[y][i]) 
    		{
    	   		x = anc[x][i];
    	   		y = anc[y][i];
    		}
    	}
    	return anc[x][0];
    }
    void dfs(int now, int f)
    {
    	dfn[++ind] = now; fi[now] = ind;
    	for (int i = head[now]; i; i = nxt[i])
    	{
    		if(v[i] == f) continue;
    		dfs(v[i], now);
    	}
    	dfn[++ind] = now; se[now] = ind;
    }
    inline void sol(int x)//原编号
    {
    	if(!vis[x]) ans += vm[a[x]] * wn[++ct[a[x]]];
    	else ans -= vm[a[x]] * wn[ct[a[x]]--];
    	vis[x] ^= 1;//翻转标记
    }
    void change()
    {
    	if(vis[c[nt][0]])
    	{
    		sol(c[nt][0]);
    		swap(a[c[nt][0]], c[nt][1]);
    		sol(c[nt][0]);
    	}
    	else swap(a[c[nt][0]], c[nt][1]);
    }
    int main()
    {
    	scanf("%d %d %d", &n, &m, &q);
    	for (int i = 1; i <= m; i++) scanf("%lld", &vm[i]);
    	for (int i = 1; i <= n; i++) scanf("%lld", &wn[i]);
    	for (int i = 1; i < n; i++)
    	{
    		int x, y;
    		scanf("%d %d", &x, &y);
    		add(x, y);
    	}
    	for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    	init();
    	dfs(1, 0);
    	block = pow(ind, 2.0 / 3.0) + 1;
    	for (int i = 1; i <= ind; i++) B[i] = (i - 1) / block + 1;
    	tx = 0, qu = 0;
    	for (int i = 1; i <= q; i++)
    	{
    		int opt; scanf("%d", &opt);
    		if(opt == 0)
    		{
    			++tx;
    			scanf("%lld %lld", &c[tx][0], &c[tx][1]);//0表示在原编号的位置,1表示修改的值
    		}
    		else 
    		{
    			++qu;
    			scanf("%d %d", &ask[qu].l, &ask[qu].r);
    			if(fi[ ask[qu].l ] > fi[ ask[qu].r ]) swap(ask[qu].l, ask[qu].r);
    			ask[qu].id = qu; ask[qu].ti = tx;
    		}
    	}
    	sort(ask + 1, ask + qu + 1, cmp);
    	for (int i = 1; i <= qu; i++)
    	{
    		while(nl < fi[ask[i].l]) sol(dfn[nl++]);//dfn记录了原编号
    		while(nl > fi[ask[i].l]) sol(dfn[--nl]);
    		while(nr < fi[ask[i].r]) sol(dfn[++nr]);
    		while(nr > fi[ask[i].r]) sol(dfn[nr--]);
    		while(nt < ask[i].ti)
    		{
    			++nt;
       			//vis 记录一个 节点计算了几次
    			change();
    		}
    		while(nt > ask[i].ti)
    		{
    			change();
    			--nt;
    		}
    		int p = LCA(ask[i].l, ask[i].r);
    		if(ask[i].l ^ p) sol(ask[i].l);
    		if(ask[i].l ^ p && ask[i].r ^ p) sol(p);
    		res[ask[i].id] = ans;
    		if(ask[i].l ^ p) sol(ask[i].l);
    		if(ask[i].l ^ p && ask[i].r ^ p) sol(p);
    	}
    	for (int i = 1; i <= qu; i++)
    	{
    		printf("%lld
    ", res[i]);
    	}
    	return 0;
    }
    
    //回滚莫队
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 200010;
    int n, m, block, bn, B[MAXN];
    int a[MAXN], nl, nr, ans, b[MAXN];
    int Ans[MAXN];
    int last[MAXN], nxt[MAXN], fir[MAXN];
    int s[MAXN], st;//记录操作
    inline int read()
    {
        int a=0,f=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
        return a*f;
    }
    inline void print(int x)
    {
        if(x<0)putchar('-'),x=-x;
        if(x<10)putchar(x+48);
        else print(x/10),putchar(x%10+48);
    }
    struct question
    {
    	int l, r, id;
    } ask[MAXN];
    inline bool cmp(const question &a, const question &b)
    {
    	return B[a.l] ^ B[b.l] ? B[a.l] < B[b.l] : a.r < b.r;
    }
    inline int max(const int &x, const int &y)
    {
    	return x > y ? x : y;
    }
    inline int min(const int &x, const int &y)
    {
    	return x < y ? x : y;
    }
    inline int calc(int l, int r)
    {
    	int res = 0;
    	for (int i = l; i <= r; i++) fir[a[i]] = 0;
    	for (int i = l; i <= r; i++)
    	{
    		if(!fir[a[i]]) fir[a[i]] = i;
    		else res = max(res, i - fir[a[i]]);
    	}
    	return res;
    }
    int main()
    {
    	n = read(); block = pow(n, 1.0 / 2.0);
    	for (int i = 1; i <= n; i++) a[i] = read(), b[i] = a[i], B[i] = (i - 1) / block + 1;
    	sort(b + 1, b + n + 1);
    	int un = unique(b + 1, b + n + 1) - b - 1;
    	for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + un + 1, a[i]) - b;
    	m = read();
    	for (int i = 1; i <= m; i++) ask[i].l = read(), ask[i].r = read(), ask[i].id = i;
    	bn = B[n];
    	sort(ask + 1, ask + m + 1, cmp);
    	for (int i = 1, j = 1; i <= bn; i++)
    	{
    		int br = min(n, i * block); 
    		nl = br + 1, nr = br, ans = 0;
    		st = 0;
    		for (;B[ask[j].l] == i; j++)
    		{
    			//last记录第一次出现,nxt记录最后一次出现
    			if(B[ask[j].r] == i)//在同一个块内暴力计算
    			{
    				Ans[ask[j].id] = calc(ask[j].l, ask[j].r);
    				continue;
    			}
    			while(nr < ask[j].r)//同一个块内r递增
    			{
    				++nr;
    				nxt[a[nr]] = nr;
    				if(!last[a[nr]]) last[a[nr]] = nr, s[++st] = a[nr];
    				ans = max(ans, nr - last[a[nr]]);
    			}
    			int tmp = ans; //先保存一下,因为右区间的贡献不会被刷新,但左区间的会
    			while(nl > ask[j].l)
    			{
    				--nl;
    				if(nxt[a[nl]]) tmp = max(tmp, nxt[a[nl]] - nl);
    				else nxt[a[nl]] = nl;
    			}
    			Ans[ask[j].id] = tmp;
    			while(nl <= br)
    			{
    				if(nxt[a[nl]] == nl) nxt[a[nl]] = 0;//去掉左区间的贡献
    				++nl;
    			}
    		}
    		for (int i = 1; i <= st; i++) last[s[i]] = nxt[s[i]] = 0;//将上一个块处理的贡献清空
    	}
    	for (int i = 1; i <= m; i++) print(Ans[i]), putchar('
    ');
    	return 0;
    }
    
  • 相关阅读:
    【noip2012】开车旅行
    【AC自动机】专题总结
    【noi2013】【bz3244】树的计数
    BZOJ1069: [SCOI2007]最大土地面积
    BZOJ1185: [HNOI2007]最小矩形覆盖
    BZOJ1047: [HAOI2007]理想的正方形
    BZOJ1801: [Ahoi2009]chess 中国象棋
    BZOJ1925: [Sdoi2010]地精部落
    BZOJ1057: [ZJOI2007]棋盘制作
    BZOJ1217: [HNOI2003]消防局的设立
  • 原文地址:https://www.cnblogs.com/hangzz/p/12385097.html
Copyright © 2011-2022 走看看