1. 程式人生 > 其它 >題解 P5629 【【AFOI-19】區間與除法】

題解 P5629 【【AFOI-19】區間與除法】

link
觀察一下本題,發現每次操作都是連續除以一個數的,除以一個數等價於把原來的數在以除以的數為進位制的表示下的末位捨棄,所以考慮轉化把進位制轉化為除以的數,可以得到一個貪心結論:只考慮一個數的最小的對應的原數即可,因為比它大的原數一定能在除以若干次後變成它。

核心思路完成了,就是怎麼實現,觀察資料範圍,發現詢問次數非常多,每次詢問就必須複雜度很低,又發現 \(m\) 相當小,可以直接狀態壓縮,採用st表進行或運算即可,可以在預處理最小原數時用字典樹來優化時間,不用也是可以過的。

code:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define int long long 
using namespace std;
const int N=5e5+100;
int n,m,d,q,f[N][30],a[N],b[100];
void ST_prework(int n)
{
	int t=log(n)/log(2)+1;
	for(int j=1;j<t;++j)
		for(int i=1;i<=n-(1<<j)+1;++i)
			f[i][j]=f[i][j-1]|f[i+(1<<(j-1))][j-1];
}
int lb(int x)
{
	return x&(-x);
}
int get(int l,int r)
{
	int k=log(r-l+1)/log(2);
	int v=f[l][k]|f[r-(1<<k)+1][k];
	int res=0;
	while(v)
	{
		++res;
		v-=lb(v);
	}
	return res;
}
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]);
	sort(b+1,b+1+m);
	for(int i=1;i<=n;++i)
	{
		int x=a[i],v=-1;
		int p=lower_bound(b+1,b+1+m,x)-b;
		if(p<=m&&b[p]==x)v=p;
		x/=d;
		while(x)
		{
			int p=lower_bound(b+1,b+1+m,x)-b;
			if(p<=m&&b[p]==x)v=p;
			x/=d;
		}
		if(v!=-1)f[i][0]=1ll<<v;
	}
	ST_prework(n);
	while(q--)
	{
		int l,r;
		scanf("%lld%lld",&l,&r);
		printf("%lld\n",get(l,r));
	}
	return 0;
}