F. Ant colony(回滚莫队)

原题链接

写在前面:

看了看其他题解,居然都是线段树,于是便有了这篇文章

。。。

思路:

题目本质是求区间 GCD 并求区间数字是 GCD 的个数

区间扩展时信息很好更新,但在缩小时信息难以更新,故考虑回滚莫队。

因为要记录数字个数,数据范围又是 1e9 区间 GCD 又不能离散化, 开 map ??? 那可以T飞了(在cf上测了下 7000ms)

现在区间只有扩展,GCD 只会不断减小 ,假如区间多了一个数 s ,区间 GCD 减小 , 可以证明原区间一定不含 s (反证法可以证明)

这样 map 就可以省了

变成回滚莫队板题了。。。

Code:

#include <iostream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <vector>
#include <string>
#include <iomanip>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
//#include <unordered_map>
#define guo312 std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
#define Inf LONG_LONG_MAX
#define inf INT_MAX
#define endl "\n"
#define PI 3.1415926535898
using namespace std;
const int N=1e5+10;

int a[N],bel[N],lk;
struct PW{
	int l,r,id;
}b[N];

bool cmp(PW s1,PW s2){
	if(bel[s1.l]!=bel[s2.l]){
		return bel[s1.l]<bel[s2.l];
	}
	else{
		return s1.r<s2.r;
	}
}

int getans(int l,int r){
	int now=0;
	for(int i=l;i<=r;i++){
		if(now==0) now=a[i];
		else now=__gcd(a[i],now);
	}
	int re=0;
	for(int i=l;i<=r;i++){
		if(a[i]==now) re++;
	}
	return r-l+1-re;
}

int ans[N];
int main(){
guo312;
	int n; cin>>n; lk=sqrt(n);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		bel[i]=(i-1)/lk+1;
	}
	int q; cin>>q;
	for(int i=1;i<=q;i++){
		cin>>b[i].l>>b[i].r;
		b[i].id=i;
	}
	sort(b+1,b+1+q,cmp);
	int now=0,last=0,l,r,now_num,last_num;
	for(int i=1;i<=q;i++){
		if(bel[b[i].l]==bel[b[i].r]){
			ans[b[i].id]=getans(b[i].l,b[i].r);
			continue;
		}
		l=bel[b[i].l]*lk;
		if(bel[b[i].l]>bel[b[i-1].l]||bel[b[i-1].l]==bel[b[i-1].r]){
			now=last=0;
			now_num=last_num=0;
			r=l-1;
		}
		now=last,now_num=last_num;
		while(r<b[i].r){
			r++;
			if(now==0){
				now_num++,now=a[r];
			}
			else{
				int s=__gcd(now,a[r]);
				if(s!=now) now=s,now_num=0;
				if(now==a[r]) now_num++;
			}
		}
		last=now,last_num=now_num;
		while(l>b[i].l){
			l--;
			if(now==0){
				now_num++,now=a[l];
			}
			else{
				int s=__gcd(now,a[l]);
				if(s!=now) now=s,now_num=0;
				if(now==a[l]) now_num++;
			}
		}
		ans[b[i].id]=b[i].r-b[i].l+1-now_num;
	}
	for(int i=1;i<=q;i++){
		cout<<ans[i]<<endl;
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

要用bug来打败bug

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

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

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

打赏作者

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

抵扣说明:

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

余额充值