P5629 【AFOI-19】区间与除法 题解
为什么题解全是状压 ST 表什么的捏。
来一个前缀和题解。
Part 1:处理原数
设
原数可能会有重复(比如样例
注意到每个原数也可能可以被其他原数消灭,设
每个能被
有结论:若
当
所以我们二次处理
处理过后,每个
这一部分总复杂度
Part 2:处理询问
经过
则有一个显然的做法:枚举每个原数,判断区间中原数是否存在,存在则答案加一。
可以先前缀和一遍,
最后输出答案即可。
这一部分复杂度为
坑点:
-
map 存原数可能会比枚举原数更慢。
-
AC代码:
// Problem: P5629 【AFOI-19】区间与除法
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5629
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// Powered by CP Editor (https://cpeditor.org)
// Auther: Sugar_Pigeon(227728)
#include <bits/stdc++.h>
using namespace std;
#define M 1000006
#define INT long long
INT n,m,ox,oy,nm,d,q,a[M],nd[M],b[M],ans,c[M],hav0;
int sum[M][65];
void calc(INT x) {
INT y=a[x];
if(hav0) {nd[x]=0;return;}
while(y) {
for(INT i=1;i<=m;i++) {
if(b[i]==y) {
nd[x]=y;
return;
}
}
y/=d;
}
if(!nd[x]) nd[x]=-1;
return;
}
bool dop(INT x) {
while(x)
{
x/=d;
for(INT i=1;i<=m;i++)
if(b[i]==x)
return 1;
}
return 0;
}
signed main() {
scanf("%lld%lld%lld%lld",&n,&m,&d,&q);
for(INT i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(INT i=1;i<=m;i++) {
scanf("%lld",&b[i]);
for(INT j=1;j<i;j++) {
if(b[j]==b[i]) {
m--,i--;
break;
}
}
}
sort(b+1,b+m+1);
for(INT i=m;i>=1;i--) {
if(!dop(b[i])) {
c[++nm]=b[i];
}
}
m=nm;
for(INT i=1;i<=m;i++)
b[i]=c[i],hav0=((hav0||!b[i])?1:0);
for(INT i=1;i<=n;i++)
calc(i);
for(INT i=1;i<=n;i++)
for(INT j=1;j<=m;j++)
sum[i][j]=sum[i-1][j]+(nd[i]==b[j]?1:0);
for(INT Q=1;Q<=q;Q++) {
scanf("%lld%lld",&ox,&oy);
ans=0;
for(INT i=1;i<=m;i++)
if(sum[oy][i]-sum[ox-1][i])
ans++;
printf("%lld\n",ans);
}
return 0;
}