洛谷 P5629 【AFOI-19】区间与除法(ST表)
传送门
解题思路
我们发现若一个原数i经过若干次op操作后能变成另一个原数j,则i没有存在的必要了(因为j更优)。
于是对于每一个ai,就能找到最小的一个原数(这个也是“性价比”最高的原数)。
因为原数数量很少,所以可以用二进制位来表示,假设消灭一个数字用到的是第i个原数,则将这个数字的值赋为2^i。
这样一群数字所需要的原数就是它们的值的异或和。
可以用ST表维护区间异或和。
怎么找每个数用哪个原数呢?
正解是建立字典树。
但是很显然可以用map+吸氧水过去。
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
map<long long,int> ma;
const int maxn=5e5+5;
int n,m,q;
long long a[maxn],b[maxn],d,st[maxn][25];
void work(int id,long long x){
while(x>0){
if(ma.find(x)!=ma.end()) st[id][0]=1ll<<ma[x];
x/=d;
}
}
int cal(long long x){
int cnt=0;
while(x>0){
cnt+=x&1;
x>>=1;
}
return cnt;
}
template<class T>inline void read(T &x)
{
x=0;register char c=getchar();register bool f=0;
while(!isdigit(c))f^=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(f)x=-x;
}
template<class T>inline void print(T x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)print(x/10);
putchar('0'+x%10);
}
template<class T>inline void print(T x,char c){print(x),putchar(c);}
int main(){
read(n);
read(m);
read(d);
read(q);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=m;i++) read(b[i]),ma[b[i]]=i;
for(int i=1;i<=n;i++){
work(i,a[i]);
}
for(int j=1;j<=20;j++){
for(int i=1;i<=n;i++){
if(i+(1<<j)-1>n) break;
st[i][j]=st[i][j-1]|st[i+(1<<(j-1))][j-1];
}
}
for(int i=1;i<=q;i++){
int l,r;
cin>>l>>r;
int j=log2(r-l+1);
long long x=st[l][j]|st[r-(1<<j)+1][j];
print(cal(x));
puts("");
}
return 0;
}