题解:CF1946F Nobody is needed
传送门
好题!我调了一下午。注意维护区间和不能用线段树,常数过大会超时,只能用树状数组。记得多测要清空。
首先可以证明在长度为
设
那么询问
如何计算 x 作为区间的左端点带来的贡献?
因为
此时贡献又分两种,一种是当
对贡献的计算可以使用 dp 来解决。设
最后将
AC code
#include <bits/stdc++.h>
#define int long long
#define lowbit(i) i&(-i)
using namespace std;
int T,n,q,dp[1000005],ans[1000005],tmp[1000005],a[1000005];
int t[1000005];
void add(int x,int c)
{
while (x<=n)
{
t[x]+=c;
x+=lowbit(x);
}
}
int get_sum(int x)
{
int sum=0;
while (x>0)
{
sum+=t[x];
x-=lowbit(x);
}
return sum;
}
int query(int l,int r)
{
return get_sum(r)-get_sum(l-1);
}
void work()
{
scanf("%d%d",&n,&q);
vector<int> r[n+1];
vector<int> no[n+1];
for (int i=1;i<=n;i++)
{
scanf("%d",a+i);
tmp[a[i]]=i;
}
for (int i=1;i<=q;i++)
{
int x,y;
scanf("%d%d",&x,&y);
r[x].push_back(y);
no[x].push_back(i);
}
for (int i=n;i>=1;i--)
{
int x=a[i];dp[x]=1;
for (int y=x;y<=n;y+=x)
{
if (tmp[y]<tmp[x]) continue;
for (int z=y*2;z<=n;z+=y)
{
if (tmp[z]<tmp[y]) continue;
dp[z]+=dp[y];
}
}
for (int j=x;j<=n;j+=x)
{
add(tmp[j],dp[j]);
dp[j]=0;
}
for (int j=0;j<r[i].size();j++)
{
ans[no[i][j]]+=query(i,r[i][j]);
}
}
for (int i=1;i<=q;i++)
{
cout<<ans[i]<<" ";
}
cout<<"\n";
for (int i=1;i<=q;i++)
{
ans[i]=0;
}
for (int i=1;i<=n;i++)
{
t[i]=0;
}
}
signed main()
{
cin>>T;
while (T--)
{
work();
}
return 0;
}