题目链接:https://codeforc.es/problemset/problem/474/F
题目大意:给定一个长度为 n n n的序列, t t t次询问区间 [ l , r ] [l,r] [l,r]内不能活下来的数量,规则为:区间内任意两个数比较,若一个数能整除另一个数,则这个数的积分+1,所有数比完之后,若当前数积分 = = r − l ==r-l ==r−l,则这个数能活下来。
思路:若这个区间内存在这样的数,那么这样的数肯定是区间最小的,且是区间其他数的因数,那么我们可以用线段树维护每次查询的区间,若某两个小区间能够合并,那么一定满足:小区间的最小数 g c d gcd gcd是其他数的倍数,并且此时要更新这个区间内最小数的数量 c n t cnt cnt。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define Mid ((l+r)>>1)
#define lson rt<<1,l,Mid
#define rson rt<<1|1,Mid+1,r
const int maxn=1e5+10;
int b[maxn];
struct node
{
int l,r,cnt,gcd;
}a[maxn<<2];
int gcd(int x,int y){
return y==0?x:gcd(y,x%y);
}
void push_up(int rt){
int t=gcd(a[rt<<1].gcd,a[rt<<1|1].gcd);
a[rt].gcd=t;
a[rt].cnt=0;
if(t==a[rt<<1].gcd){
a[rt].cnt+=a[rt<<1].cnt;
}
if(t==a[rt<<1|1].gcd){
a[rt].cnt+=a[rt<<1|1].cnt;
}
}
void build(int rt,int l,int r){
a[rt].l=l;
a[rt].r=r;
if(l==r){
a[rt].gcd=b[l];
a[rt].cnt=1;
return ;
}
build(lson);
build(rson);
push_up(rt);
}
typedef pair<int,int> pii;
pii query ( int u , int left , int right )
{
int l = a[u].l;
int r = a[u].r;
if ( left <= l && r <= right )
return make_pair ( a[u].gcd , a[u].cnt );
pii ret,temp;
ret.first = -1;
int mid = l+r>>1;
if ( left <= mid && right >= l )
ret = query ( u<<1 , left , right );
if ( left <= r && right > mid )
{
temp = query ( u<<1|1 ,left , right );
if ( ret.first == -1 ) ret = temp;
else
{
int d = gcd ( ret.first , temp.first );
if ( d != ret.first ) ret.second = 0;
if ( d == temp.first ) ret.second += temp.second;
ret.first = d;
}
}
return ret;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
}
build(1,1,n);
int t;
scanf("%d",&t);
while(t--){
int l,r;
scanf("%d%d",&l,&r);
pii tmp=query(1,l,r);
int ans=r-l+1-tmp.second;
printf("%d\n",ans);
}
return 0;
}