• JZOJ6641. 【GDOI20205.20模拟】sequence


    Description

    在这里插入图片描述
    在这里插入图片描述

    Solution

    • 首先你需要感受出一种贪心地方法:如果p<q,那么全部都放1肯定最优,否则考虑从1到n一个一个放,如果能往右放就往右放。
    • 对于p<=qp<=q的情况,相当于求i=1niksum_{i=1}^{n}i^k,拉格朗日插值可以做到O(k)O(k)求一次。
    • 接下来考虑p>qp>q
    • 那么相当于从右往左放,每一次放的尽量多,可以二分放多少,再O(m)O(m)判断。由于长度是2nsqrt{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
    ",(Get(n,k+1)+mo)%mo); 
    		} else printf("%lld
    ",doit());
    	}
    }
    
    
  • 相关阅读:
    .NET Core微服务之基于Consul实现服务治理
    在 .NET 4.5 中反射机制的变更
    C#使用Emit构造拦截器动态代理类
    C#使用Emit生成构造函数和属性
    秒懂C#通过Emit动态生成代码
    C# 高性能 TCP 服务的多种实现方式
    Zookeeper的功能以及工作原理
    Eclipse智能提示及快捷键
    【Maven】Select Dependency 无法检索
    springboot的三种启动方式
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090884.html
Copyright © 2020-2023  润新知