Codeforces Round #182 (Div. 1) D. Yaroslav and Divisors(树状数组+思维)

题目链接
在这里插入图片描述
在这里插入图片描述
题意:给定初始数组,m个询问,每次询问问你【l,r】内一个数是另一个数的因子,求数量。
思路:我们一开始是想按右端点排序,然后计算以下query(s【i】.r)-query(s【i】.l-1)就行了,但是这样有一个问题,我们忘了减去【1,l-1】里的每个的每个数对【l,r】里的数产生的贡献了,但是这个贡献不好直接减,于是有一个神乎奇迹的解法,就是再这一个数组,s1维护的是左端点,s2维护的是右端点,当i作为左端点的时候减去【1,l-1】里的每个的每个数对【l,r】里的数产生的贡献,当i作为右端点时就是用我们一开始的做法直接算就好了,因为这个时候该减的都已经减了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
#define lowbit(i)  (i)&(-i)
int a[maxn],pos[maxn];
ll c[maxn],ans[maxn];
struct node{
	int l,r,id;
}s1[maxn],s2[maxn];
bool cmp1(const node &a,const node &b)
{
	return a.l==b.l?a.r<b.r:a.l<b.l;
}
bool cmp2(const node &a,const node &b)
{
	return a.r==b.r?a.l<b.l:a.r<b.r;
}
void update(int x,int val)
{
	while(x<maxn) c[x]+=val,x+=lowbit(x);
}
ll query(int x)
{
	ll res=0;
	while(x>0) res+=c[x],x-=lowbit(x);
	return res;
}
int main()
{
	int n,m,maxx=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
	scanf("%d",&a[i]),pos[a[i]]=i,maxx=max(maxx,a[i]);
	for(int i=1;i<=m;++i)
	{
		scanf("%d %d",&s1[i].l,&s1[i].r);
		s1[i].id=i;
		s2[i]=s1[i];
	}
	sort(s1+1,s1+1+m,cmp1);
	sort(s2+1,s2+1+m,cmp2);
	int pos1=1,pos2=1;
	for(int i=1;i<=n;++i)
	{
		while(pos1<=m&&s1[pos1].l==i) ans[s1[pos1].id]-=query(s1[pos1].r)-query(s1[pos1].l-1),pos1++;
		for(int j=a[i];j<=maxx;j+=a[i]) update(pos[j],1);
		while(pos2<=m&&s2[pos2].r==i) ans[s2[pos2].id]+=query(s2[pos2].r)-query(s2[pos2].l-1),pos2++;
	}
	for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值