洛谷 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;
}
原文地址:https://www.cnblogs.com/yinyuqin/p/15330606.html