题干:

其中 zi 是第i次询问后的z。

解题报告:

    因为有取log运算,所以分母的取值肯定不会超过30种,所以分每一个分母的时候,用前缀和优化一个和,最后求乘积就行了。(其实不需要快速幂,用快速幂也可以但是容易出错,因为需要判断如果已经大于1e9了就直接return到一个break的地方,但是wjh大佬强啊!!所以不怂、、)

另外这题要注意不能直接一个前缀和求出来之后向下取整,举个例子,1/3+1/3+1/3+1/3+1/3+1/3+1/3,正确答案应该是0,但是你这样直接分子前缀和求得的结果就是2,所以应该维护30个向下取整前缀和,就巧妙的优化了这个问题。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define F first
#define S second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 5e5 + 5;
const ll mod = 1e9;
ll a[MAX],p[MAX];
ll all[MAX][31];
ll qsm(ll a,ll b,int &ff) {
	ll t=1;
	int fl=0;
	while(b) {
		if(b&1) {
			t*=a;
			if(t>mod||fl) {
				ff=1;break;
			}
			t%=mod;
		}
		a*=a;
		if(a>mod) fl=1;
		a%=mod;
		b>>=1;
	}
	return t;
}
int main() {
	int t;
	cin>>t;
	while(t--) {
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1; i<=n; i++) {
			scanf("%lld",&a[i]);
		}
		sort(a+1,a+n+1);
		for(int j = 1; j<=30; j++) {
			for(int i=1; i<=n; i++) {
				all[i][j]=all[i-1][j]+a[i]/j;
			}
		}
		ll ans=0;
		for(ll i=1; i<=m; i++) {
			ll tmp=0;
			scanf("%lld",&p[i]);
			for(ll j=1; j<=n;) {
				int ff=0;
				ll tt=(ll)ceil(log(a[j]*1.0)*1.0/log(p[i]*1.0));//求出大分母 
				ll maxx=qsm(p[i],tt,ff);
				if(ff) {
					ll sum1=all[n][tt]-all[j-1][tt];
					tmp=(tmp+sum1)%mod;
					break;
				}
				int pos=upper_bound(a+1,a+n+1,maxx)-a - 1; //找到最后一个小于等于他的。 
				ll sum=all[pos][tt]-all[j-1][tt];
				tmp=(tmp+sum)%mod;
				j=pos+1;
			}
			ans=(ans+i*tmp)%mod;
		}
		printf("%lld\n",ans%mod);
	}

	return 0;
}