题目链接
题意:给定n个数,问有多少区间【l,r】满足区间内数的乘积是k的倍数。
思路:写了半天的二分居然超时了。。。
还是用了线段树,为了防止爆ll可以乘积%k,然后对于每一个位置进行二分,如果【li,ri】是k的倍数的话,不用说【li,ri+1】,【li,ri+2】。。。肯定也是满足的,二分查找第一个满足条件的右端点就行。
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long ll;
struct node{
int l,r;
ll sum;
}tree[maxn<<2];
ll ans=0,k,a[maxn];
void pushup(int x)
{
tree[x].sum=(tree[x<<1].sum%k)*(tree[x<<1|1].sum%k)%k;
}
void build(int x,int l,int r)
{
tree[x].l=l;tree[x].r=r;
if(l==r) {
tree[x].sum=a[l];return ;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
}
ll query(int x,int l,int r)
{
if(l<=tree[x].l&&tree[x].r<=r) return (tree[x].sum)%k;
int mid=(tree[x].l+tree[x].r)>>1;
ll ans=1;
if(l<=mid) ans=(ans*query(x<<1,l,r))%k;
if(mid<r) ans=(ans*query(x<<1|1,l,r))%k;
return ans%k;
}
int main()
{
int n;
scanf("%d %lld",&n,&k);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
build(1,1,n);
for(int i=1;i<=n;++i)
{
int l=i,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(query(1,i,mid)%k==0) r=mid-1;
else l=mid+1;
}
if(query(1,i,l)%k==0) ans+=n-l+1;
}
printf("%lld\n",ans);
}