JZOJ6641. 【GDOI20205.20模擬】sequence

Description

在這裏插入圖片描述
在這裏插入圖片描述

Solution

  • 首先你需要感受出一種貪心地方法:如果p<q,那麼全部都放1肯定最優,否則考慮從1到n一個一個放,如果能往右放就往右放。
  • 對於p<=qp<=q的情況,相當於求i=1nik\sum_{i=1}^{n}i^k,拉格朗日插值可以做到O(k)O(k)求一次。
  • 接下來考慮p>qp>q
  • 那麼相當於從右往左放,每一次放的儘量多,可以二分放多少,再O(m)O(m)判斷。由於長度是2n\sqrt{2n}級別的,所以這個複雜度O(m2logn)O(m^2logn)並不能過。
  • 結合第一種做法,二分出最右的至少能放1的位置,再二分最多能放多少個,把這個位置填最多,以及之前的位置都填最少的量。
  • 如果這個位置每填1,前面就需要多填kk,如果不考慮上取整的關係,就可以近似這麼認爲,那麼resres就會變成res mod kres\ mod\ k,因爲數據隨機,可以近似這麼看,所以總的輪數實際上是O(logn)O(log n)的。
  • O(m logn2)O(m\ logn^2).
#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());
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章