普通莫队

普通莫队

形式

对于n个数的序列,我们要对m个区间进行询问,当我们已知 [ l , r ] [l,r] [l,r]可以在 O ( 1 ) O(1) O(1)的复杂度求出 [ l , r − 1 ] , [ l , r + 1 ] , [ 1 − 1 , r ] , [ l + 1 , r ] [l,r-1],[l,r+1],[1-1,r],[l+1,r] [l,r1],[l,r+1],[11,r],[l+1,r],我们可以用莫队算法求解复杂度 O ( n n ) O(n\sqrt n) O(nn )

主要用在对多个区间进行询问的离线算法(可以和很多算法结合起来)。

复杂度分析:见 o i − w i k i oi-wiki oiwiki

普通版本

我们将要查询的序列分组,然后对查询的区间 [ l , r ] [l,r] [l,r]进行排序:排序规则为先按 l l l所在分组的编号进行排序,如果在同一组就按 r r r的大小进行排序。

bool cmp(Y x, Y y) {
	if(x.l/block != y.l/block) return x.l < y.l;
	else return x.r < y.r;
}

奇偶化优化版本

先按 l l l所在的块进行排序,如果在同一块中,假如这个块是奇数块就按从小到大进行排序,偶数块就按从大到小进行排序,快 30 % 30\% 30%左右。

bool cmp1(Y x, Y y) {
    if(x.l/block != y.l/block) return x.l < y.l;
    if((x.l/block) & 1) return x.r < y.r;
    return x.r > y.r;
}

题目

P2709小B的询问

公式推导:
∑ i = 1 k c i 2 当 其 中 项 变 成 ( c i + 1 ) 2 = c i 2 + 2 c i + 1 贡 献 加 了 2 c i + 1 。 \sum_{i=1}^kc_i^2\\ 当其中项变成(c_i+1)^2=c_i^2+2c_i+1\\ 贡献加了2c_i+1。 i=1kci2(ci+1)2=ci2+2ci+12ci+1

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4+10;
int s[N], block, cnt[N], A[N];
struct Y {
	int l, r, i;
	Y (int x=0, int y=0, int z=0) : l(x), r(y), i(z){}
	
} a[N];
//普通分块
bool cmp(Y x, Y y) {
	if(x.l/block != y.l/block) return x.l < y.l;
	else return x.r < y.r;
}
bool cmp1(Y x, Y y) {
    if(x.l/block != y.l/block) return x.l < y.l;
    if((x.l/block) & 1) return x.r < y.r;
    return x.r > y.r;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
	int n, m, k, x, y;
	cin >> n >> m >> k;
	block = ceil(sqrt(n));
	for(int i=1; i<=n; i++) {
		cin >> s[i];
	}
	for(int i=1; i<=m; i++) {
		cin >> x >> y;
		a[i] = Y(x, y, i);
	}
	sort(a+1, a+1+m, cmp);
	int l=1, r=0, ans=0;
	for(int i=1; i<=m; i++) {
		while(l > a[i].l) l--, cnt[s[l]]++, ans += 2*cnt[s[l]]-1;
		while(r < a[i].r) r++, cnt[s[r]]++, ans += 2*cnt[s[r]]-1;
		while(l < a[i].l) cnt[s[l]]--, ans -= 2*cnt[s[l]]+1, l++; 
		while(r > a[i].r) cnt[s[r]]--, ans -= 2*cnt[s[r]]+1, r--;
		A[a[i].i] = ans;
	}
	for(int i=1; i<=m; i++)  cout << A[i] << endl;
	return 0;
}


P1494[国家集训队]小Z的袜子(维护cnt来求概论)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4+10;
ll s[N], u[N], v[N], cnt[N], block;
struct Y {
	ll l, r, i;
	Y(int x=0, int y=0, int z=0):l(x), r(y), i(z) {}
} a[N];
//对于不同块的区间,按所在块的顺序进行排序
//对于同一块的区间,按右端点的值进行排序
//普通分块
bool cmp(Y a, Y b) {
	if(a.l/block != b.l/block) return a.l < b.l;
	else return a.r < b.r;
}
//奇偶化分块
bool cmp1(Y a, Y b) {
    if(a.l/block != b.l/block) return a.l < b.l;
    if((a.l/block) & 1) return a.r < b.r;
    return a.r > b.r;
}
ll cal(ll x) {
	return x*(x-1)/2;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
	ll n, m, x, y, z;
	cin >> n >> m;
	block = ceil(sqrt(n));
	for(int i=1; i<=n; i++) {
		cin >> s[i];
	}
	for(int i=1; i<=m; i++) {
		cin >> x >> y;
		a[i] = Y(x, y, i);
	}
	
	sort(a+1, a+1+m, cmp);
	ll l = 1, r = 0, ans = 0;
	for(int i=1; i<=m; i++) {
		while(l > a[i].l) {
			l--, cnt[s[l]]++;
			ans = ans - cal(cnt[s[l]]-1) + cal(cnt[s[l]]);
		}
		while(r < a[i].r) {
			r++, cnt[s[r]]++;
			ans = ans - cal(cnt[s[r]]-1) + cal(cnt[s[r]]);
		}
		while(l < a[i].l) {
			cnt[s[l]]--;
			ans = ans - cal(cnt[s[l]]+1) + cal(cnt[s[l]]);
			l++;
		}
		while(r > a[i].r) {
			cnt[s[r]]--;
			ans = ans - cal(cnt[s[r]]+1) + cal(cnt[s[r]]);
			r--;
		}
		u[a[i].i] = ans, v[a[i].i] = cal(a[i].r-a[i].l+1);
	}
	for(int i=1; i<=m; i++) {
		if(u[i] == 0) cout << "0/1\n";
		else {
			int k = __gcd(u[i], v[i]);
			cout << u[i]/k << '/' << v[i]/k << endl;
		}
	}
	return 0;
}

P3709大爷的字符串题

c n t cnt cnt t t t来维护区间最多出现的元素出现了几次

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5+10;
int block, ans = 0;
int s[N], cnt[N], t[N], res[N];
struct Y {
	ll l, r, i;
	Y(int x=0, int y=0, int z=0):l(x), r(y), i(z) {}
} a[N];
struct X{
    int id, x;
} b[N];
bool c(X a, X b) {
    return a.x < b.x;
}
//奇偶化分块
bool cmp1(Y a, Y b) {
    if(a.l/block != b.l/block) return a.l < b.l;
    if((a.l/block) & 1) return a.r < b.r;
    return a.r > b.r;
}
void add(int x) {
    t[cnt[x]]--;
    cnt[x]++;
    t[cnt[x]]++;
    ans = max(cnt[x], ans);
}
void del(int x) {
    t[cnt[x]]--;
    if(t[cnt[x]] == 0 && ans == cnt[x]) ans--;
    cnt[x]--;
    t[cnt[x]]++;
}

int main() {
#ifndef ONLINE_JUDGE 
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif 
    int n, m, x, y, t = 1;
    scanf("%d%d", &n, &m);
    block = ceil(sqrt(n));
    for(int i=1; i<=n; i++) {
        cin >> b[i].x;
        b[i].id = i;
    }
    sort(b+1, b+1+n, c);
    s[b[1].id] = 1;
    for(int i=2; i<=n; i++) {
        if(b[i].x != b[i-1].x) t++;
        s[b[i].id] = t;
    }
    // for(int i=1; i<=n; i++) cout << s[i] << endl;
    for(int i=1; i<=m; i++) {
        scanf("%d%d", &x, &y);
        a[i] = Y(x, y, i);
    }
    int l = 1, r = 0;
    sort(a+1, a+1+m, cmp1);
    for(int i=1; i<=m; i++) {
        while(l > a[i].l) l--, add(s[l]);
        while(r < a[i].r) r++, add(s[r]);
        while(l < a[i].l) del(s[l]), l++;
        while(r > a[i].r) del(s[r]), r--;

        res[a[i].i] = ans;
    }
    for(int i=1; i<=m; i++) printf("%d\n", -res[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值