莫队

区间求和

  该莫队以洛谷P1972 [SDOI2009]HH的项链为例。直接求和的莫队不需要cnt数组。
  设置两个移动指针left和right,进行移动。若区间发生扩充,则加上移动位置的数组值,若区间发生缩减,则减去移动位置上数组的值。代码如下:

#define MAX 1e6
int init[MAX], cnt[MAX], now = 0, left = 1, right = 0;  //init为原始数据,cnt为当前数值出现次数,now记录和

void add(int position){  //扩充时
    if(!cnt[init[position]]) ++now;
    ++cnt[init[position]];
}

void del(int position){  //缩减时
    --cnt[init[position]];
    if(!cnt[init[position]]) --now;
}

void work() {//优化2主过程
    for(int i = 1; i <= q; ++i) {//对于每次询问
        int ql, qr;
        scanf("%d%d", &ql, &qr);//输入询问的区间
        while(left < ql) del(left++);//如左指针在查询区间左方,左指针向右移直到与查询区间左端点重合
        while(left > ql) add(--left);//如左指针在查询区间左端点右方,左指针左移
        while(right < qr) add(++right);//右指针在查询区间右端点左方,右指针右移
        while(right > qr) del(right--);//否则左移
        printf("%d\n", now);//输出统计结果
    }
}

优化

NO.1

由于使用莫队时,通常数据量会比较大,因此需要使用快读,如下:

int read(){
	char ch = getchar();
	int res = 0;
	bool flag = false;
	while((ch < '0' || ch > '9') && ch != '-') ch = getchar();
	if(ch == '-') flag = true;
	while(ch >= '0' && ch <= '9'){
		res += res * 10 + ch - '0';
		ch = getchar();
	}
	if(flag) res *= -1;
	return res;
}
NO.2

对原始数组按照端点顺序进行排序:左端点不同时,按照左端点从小到大排序;左端点相同时,若左端点为奇数,则按右端点升序排序,若左端点为偶数,则按右端点降序排序。sort()函数的cmp()部分如下:

struct visit{
	int left, right;  //记录访问区间的左右端点值
}visits[MAX];

bool cmp(struct visit a, struct visit b){
	if(a.left != b.left) return a.left < b.left;  //左端点不同,按左端点升序排
	else if(a.left & 1) return a.right < b.right;  //左端点相同且为奇数,按右端点升序排
	else return a.right > b.right;  //左端点相同且为偶数,按右端点降序排
}
NO.3

开启O2优化#pragma GCC optimize(2)

NO.4

由于指针移动过程中,需要不断调用add()del()函数,所以比较耗时。因此利用符号优先级去掉两个函数的调用过程,直接在while中实现加减过程,如下:

while(left < ql) now -= !--cnt[init[left++]];
while(left > ql) now += !cnt[init[--left]]++;
while(right < qr) now += !cnt[init[++right]]++;
while(right > qr) now -= !--cnt[init[right--]];

参考资料

莫队算法——从入门到黑题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

D-A-X

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值