Description
Solution
- 首先你需要感受出一種貪心地方法:如果p<q,那麼全部都放1肯定最優,否則考慮從1到n一個一個放,如果能往右放就往右放。
- 對於的情況,相當於求,拉格朗日插值可以做到求一次。
- 接下來考慮。
- 那麼相當於從右往左放,每一次放的儘量多,可以二分放多少,再判斷。由於長度是級別的,所以這個複雜度並不能過。
- 結合第一種做法,二分出最右的至少能放1的位置,再二分最多能放多少個,把這個位置填最多,以及之前的位置都填最少的量。
- 如果這個位置每填1,前面就需要多填,如果不考慮上取整的關係,就可以近似這麼認爲,那麼就會變成,因爲數據隨機,可以近似這麼看,所以總的輪數實際上是的。
- .
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define ll long long
#define mo 1000000007
#define maxn 1000005
#define maxm 50005
using namespace std;
int T,i,j,k,p,q,n,m,a[maxm],b[maxm];
ll fct[maxn],invf[maxn];
ll ksm(ll x,ll y){
ll s=1;
for(;y;y/=2,x=x*x%mo) if (y&1)
s=s*x%mo;
return s;
}
int tot,pri[maxn],bz[maxn]; ll f[maxn];
void getpri(int n,int k){
memset(bz,0,sizeof(bz)),tot=0,f[1]=1;
for(int i=2;i<=n;i++){
if (!bz[i]) pri[++tot]=i,f[i]=ksm(i,k);
for(j=1;j<=tot&&i*pri[j]<=n;j++){
bz[i*pri[j]]=1,f[i*pri[j]]=f[i]*f[pri[j]]%mo;
if (i%pri[j]==0) break;
}
}
for(int i=1;i<=n;i++) f[i]=(f[i]+f[i-1])%mo;
}
ll pre[maxn],nex[maxn];
ll Get(int n,int k){
pre[0]=1,nex[k]=1;
for(i=1;i<=k;i++) pre[i]=pre[i-1]*(n-i+1)%mo;
for(i=k-1;i>=0;i--) nex[i]=nex[i+1]*(n-i-1)%mo;
ll sum=0;
for(i=0;i<=k;i++) {
if ((k-i)&1)
sum-=f[i]*pre[i]%mo*nex[i]%mo*invf[i]%mo*invf[k-i]%mo;
else sum+=f[i]*pre[i]%mo*nex[i]%mo*invf[i]%mo*invf[k-i]%mo;
}
return sum%mo;
}
int check(int x,int d){
for(int i=1;i<=m;i++) b[i]=a[i];
b[x]+=d; ll sum=d;
for(int i=x-1;i>=1&&1ll*b[i]*q<1ll*b[i+1]*p;i--){
sum+=ceil(1.0*b[i+1]*p/q)-b[i];
if (sum>n) return 0;
b[i]=ceil(1.0*b[i+1]*p/q);
}
return 1;
}
void put(int x,int d){
a[x]+=d,n-=d;
for(int i=x-1;i>=1&&1ll*a[i]*q<1ll*a[i+1]*p;i--){
n-=ceil(1.0*a[i+1]*p/q)-a[i];
a[i]=ceil(1.0*a[i+1]*p/q);
}
}
ll doit(){
memset(a,0,sizeof(a));
m=sqrt(n*2); int tp=1;
while (n){
int l=1,r=m,mid,id=0,mx;
while (l<=r) {
mid=(l+r)>>1;
if (check(mid,1)) l=mid+1,id=mid;
else r=mid-1;
}
if (tp) m=id,tp=0;
l=1,r=n,mx=0;
while (l<=r) {
mid=(l+r)>>1;
if (check(id,mid)) l=mid+1,mx=mid;
else r=mid-1;
}
put(id,mx);
}
ll ans=0;
for(i=1;i<=m;i++)
ans+=ksm(i,k)*a[i]%mo;
return ans%mo;
}
int main(){
scanf("%d",&T);
fct[0]=1; for(i=1;i<maxn;i++) fct[i]=fct[i-1]*i%mo;
invf[maxn-1]=ksm(fct[maxn-1],mo-2);
for(i=maxn-2;i>=0;i--) invf[i]=invf[i+1]*(i+1)%mo;
while (T--){
scanf("%d%d%d%d",&n,&k,&p,&q);
if (p<=q) {
getpri(k+1,k);
printf("%lld\n",(Get(n,k+1)+mo)%mo);
} else printf("%lld\n",doit());
}
}