【GDOI20205.20模擬】Sequence

題目

Description
在這裏插入圖片描述

Input
第一行一個整數 T,表示一共 T 組數據。
接下來一共 T 行,每一行 4 個整數 n, k, p, q,意義如題。

Output
一共 T 行。第 i 行一個整數表示第 i 組數據的答案

Sample Input
6
10 1 2 1
20 2 3 2
30 3 7 3
100000000 100 1616 2020
987 4 132 76
98765 3 256 98

Sample Output
15
113
220
928450828
225764
1117459

樣例解釋
前三組數據最優的 a 分別爲:
a = {6, 3, 1}
a = {9, 5, 3, 2, 1}
a = {19, 7, 3, 1}

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

思路

對於p/q<=1
顯然,全部填1是最優的
於是,就是一個典型的自然數冪求和問題,用拉格朗日插值法做到O(k)

對於p/q>1
序列長度一定小於sqrt(2*n)
首先考慮如何使答案最大
對於這個序列,每次找到最大的位置,使其+1也滿足條件,做n次。
顯然這樣太慢了(O(n))

可以發現有些操作可以一起做
考慮枚舉從後往前每一個位置,二分每個位置能加多少。每填完一個數要用最小代價使序列合法
設序列長度是c,這樣做時間複雜度O(c*c log n),可以通過p/q>1.1的測試點

可以發現枚舉中間會有一大段的+0,考慮跳過這些操作
我們可以用二分來解決這個問題
二分一個滿足至少能+1的位置,再二分加多少即可
複雜度有些玄學,不過數據近似隨機,所以大概應該是
在這裏插入圖片描述

代碼


#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int INF=0x3f3f3f3f;
const ll LLINF=0x3f3f3f3f3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-4;
const int MAX=1e6+10;
const ll mod=1e9+7;
const int D=1000105;
ll f[D],g[D],p1[D],p2[D],b[D];

ll power(ll x,ll t)
{
	ll b=1;
	while(t)
	{
		if(t&1) b=b*x%mod;
		x=x*x%mod; t>>=1;
	}
	return b;
}
namespace polysum
{
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
	ll powmod(ll a,ll b)
	{
		ll res=1;
		a%=mod;
		assert(b>=0);
		for(; b; b>>=1)
		{
			if(b&1)res=res*a%mod;
			a=a*a%mod;
		}
		return res;
	}
	ll calcn(int d,ll *a,ll n)   // a[0].. a[d] ?a[n]?
	{
		if (n<=d) return a[n];
		p1[0]=p2[0]=1;
		rep(i,0,d+1)
		{
			ll t=(n-i+mod)%mod;
			p1[i+1]=p1[i]*t%mod;
		}
		rep(i,0,d+1)
		{
			ll t=(n-d+i+mod)%mod;
			p2[i+1]=p2[i]*t%mod;
		}
		ll ans=0;
		rep(i,0,d+1)
		{
			ll t=g[i]*g[d-i]%mod*p1[i]%mod*p2[d-i]%mod*a[i]%mod;
			if ((d-i)&1) ans=(ans-t+mod)%mod;
			else ans=(ans+t)%mod;
		}
		return ans;
	}
	void init(int M)
	{
		f[0]=f[1]=g[0]=g[1]=1;
		rep(i,2,M+5) f[i]=f[i-1]*i%mod;
		g[M+4]=powmod(f[M+4],mod-2);
		per(i,1,M+4) g[i]=g[i+1]*(i+1)%mod;
	}
	ll polysum(ll m,ll *a,ll n)   // a[0].. a[m] \sum_{i=0}^{n-1} a[i]
	{
		for(int i=0; i<=m; i++) b[i]=a[i];
		b[m+1]=calcn(m,b,m+1);
		rep(i,1,m+2) b[i]=(b[i-1]+b[i])%mod;
		return calcn(m+1,b,n-1);
	}
} // polysum::init();
ll quick_mod(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=(ans*a)%mod;
		a=(a*a)%mod;
		b/=2;
	}
	return ans%mod;
}
ll a[1000005];
ll work(ll n,ll k)
{
	a[0]=0;
	for(int i=1;i<=k+2;i++)
	{
		a[i]=(quick_mod(i,k))%mod;
	}
	ll ans=polysum::polysum(k+2,a,n+1)%mod;
	return ans;
}
const int N=1e6+77;
ll n,k,p,q,x[N],y[N],now;
ll calc(ll x)
{
	ll yjy=x*p,aii=0;
	if(yjy%q>0) aii=1;
	return yjy/q+aii;
}
bool check(int t,ll val)
{
	int nn=sqrt(2*n);
	ll nnow=0;
	for(int i=1; i<=nn; i++) y[i]=x[i],nnow+=x[i];
	y[t]+=val; nnow+=val;
	for(int i=t-1; i>=1; i--)
	{
		y[i]=calc(y[i+1]),nnow=nnow+y[i]-x[i];
		if(nnow>n) return 0;
	}
	if(nnow<=n) return 1;
	return 0;
}
void solve(int t,ll val)
{
	int nn=sqrt(2*n);
	x[t]+=val;
	for(int i=t-1; i>=1; i--) x[i]=calc(x[i+1]);
	now=0;
	for(int i=1; i<=nn; i++) now+=x[i];
}
int main()
{
	freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout);
	polysum::init(N-77);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld%lld%lld%lld",&n,&k,&p,&q);
		if(p<=q) printf("%lld\n",work(n,k));
		else
		{
			now=0;
			memset(x,0,sizeof(x));
			while(now<n)
			{
				int l=1,r=sqrt(n*2),yjy=0;
				while(l<=r)
				{
					int mid=l+r>>1;
					if(check(mid,1)) yjy=mid,l=mid+1;else r=mid-1;
				}
				l=1,r=n-now; ll aii=0;
				while(l<=r)
				{
					int mid=l+r>>1;
					if(check(yjy,mid)) aii=mid,l=mid+1;else r=mid-1;
				}
				solve(yjy,aii);
			}
			ll ans=0;
			for(int i=1; i<=sqrt(2*n); i++) ans=(ans+power(i,k)*x[i]%mod)%mod;
			printf("%lld\n",ans);
		}
	}
}












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