zoukankan      html  css  js  c++  java
  • [SDOI2013] 淘金

    题目描述

    小Z在玩一个叫做《淘金者》的游戏。游戏的世界是一个二维坐标。X轴、Y轴坐标范围均为1..N。初始的时候,所有的整数坐标点上均有一块金子,共N*N块。

    一阵风吹过,金子的位置发生了一些变化。细心的小Z发现,初始在(i,j)坐标处的金子会变到(f(i),fIj))坐标处。其中f(x)表示x各位数字的乘积,例如f(99)=81,f(12)=2,f(10)=0。

    如果金子变化后的坐标不在1..N的范围内,我们认为这块金子已经被移出游戏。同时可以发现,对于变化之后的游戏局面,某些坐标上的金子数量可能不止一块,而另外一些坐标上可能已经没有金子。这次变化之后,游戏将不会再对金子的位置和数量进行改变,玩家可以开始进行采集工作。

    小Z很懒,打算只进行K次采集。每次采集可以得到某一个坐标上的所有金子,采集之后,该坐标上的金子数变为0。

    现在小Z希望知道,对于变化之后的游戏局面,在采集次数为K的前提下,最多可以采集到多少块金子? 答案可能很大,小Z希望得到对1000000007(10^9+7)取模之后的答案。

    输入输出格式

    输入格式:

    共一行,包含两介正整数N,K。

    输出格式:

    一个整数,表示最多可以采集到的金子数量。

    输入输出样例

    输入样例#1: 
    12  5
    输出样例#1: 
    18

    说明

    N < = 10^12 ,K < = 100000

    对于100%的测试数据:K < = N^2

    (为什么我的代码又这么长啊2333,到底是因为 码风不再简约 还是因为最近做的题目都太毒瘤了啊??)

    一:统计一维的点的答案

        首先可以发现 f() 的取值并不是很多,因为其的质因子集合只能是 {2,3,5,7} 的一个子集,搜一搜发现n=1e12的时候 f() 的取值只有不到 15000 种。。。

        于是就可以直接数位dp啦,f[i][j][01] 表示考虑了从高到低前i为,目前数字的乘积是 num[j] ,并且是(1)否(0)贴上界的数的个数有多少。

        可以先预处理一个数组 to[i][1~9],表示num[i] * 1~9 会到 num[to[i][0~9]],转移的时候根据这个数组转移就好啦。。。。

        不过还需要增设一个状态0,表示目前还没有选数的状态(因为选的数可能位数比n少),0状态只能从自己过来,并且出边和num[] = 1的状态是一样的。

        

        所以这个数位dp貌似是 O(12 * 15000 * 9)的? 实际还要小一点,因为加了一些小剪枝。。。。

    二:一维转化成二维,计算最后答案

        我们设最后 f(x) = num[i] 的 x 有 c[i] 个,那么 最后的问题就是,选k对 (i,j) ,使得 ∑ c[i] * c[j] 最大。

        这貌似是一个 二分+two pointer 的模板题,,只不过因为c[] * c[]在大数据会爆long long,所以我取了一下对数,把整数乘法变成了实数加法。

         按理说实数运算总是会有点误差的,更何况我还没用long double,但最后莫名其妙的就A 了2333,并且还是bzoj 这个题的rank1,好迷啊。。。。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const double eps=1e-11;
    const int maxn=15005,ha=1e9+7;
    inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
    inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
    int to[maxn][10],k,ans,cnt,a[15],len,hz[maxn];
    ll n,num[maxn],f[15][maxn][2],c[maxn];
    double b[maxn],L,R,mid;
    
    inline void init(){
    	for(ll A=1;A<=n;A*=7ll)
    	    for(ll B=A;B<=n;B*=5ll)
    	        for(ll C=B;C<=n;C*=3ll)
    	            for(ll D=C;D<=n;D*=2ll) num[++cnt]=D;
    	
    	sort(num+1,num+cnt+1);
    	
    	for(int i=1;i<=cnt;i++){
    		to[i][1]=i;
    		ll T;
    		for(int j=2;j<=9;j++){
    			T=num[i]*(ll)j;
    			if(T>num[cnt]) break;
    			to[i][j]=lower_bound(num+1,num+cnt+1,T)-num;
    		}
    	}
    	
    	for(int i=1;i<=9;i++) to[0][i]=i;
    }
    
    inline void dp(){
    	while(n) a[++len]=n%10,n/=10;
    	reverse(a+1,a+len+1);
    	
    	f[0][0][1]=1;
    	for(int i=1;i<=len;i++){
    		
    		f[i][0][0]=f[i-1][0][1]+f[i-1][0][0];
    		
    	    for(int j=0;j<=cnt;j++) if(f[i-1][j][0]||f[i-1][j][1]){
    	    	
    	    	for(int l=1,T;l<=9;l++){
    	    		T=to[j][l];
    	    		if(!T) continue;
    	    		
    	    		f[i][T][0]+=f[i-1][j][0];
    	    		if(l<a[i]) f[i][T][0]+=f[i-1][j][1];
    	    		else if(l==a[i]) f[i][T][1]+=f[i-1][j][1];
    			}
    		}
    		
    	}
    	
    }
    
    inline ll count(){
    	ll an=0;
    	int l=1;
    	
    	for(int i=cnt;i;i--){
    		while(l<cnt&&b[l]+b[i]+eps<mid) l++;
    		if(b[l]+b[i]+eps<mid) break;
    		an+=(ll)(cnt-l+1);
    	}
    	
    	return an;
    }
    
    inline void calc(){
    	for(int i=1;i<=cnt;i++) c[i]=f[len][i][0]+f[len][i][1];
    	sort(c+1,c+cnt+1);
    	for(int i=1;i<=cnt;i++){
    	    if(c[i]) b[i]=log(c[i]);
    	    else b[i]=-233;
    	    R=max(R,b[i]);
    	}
    	
    	L=-233;R*=2;
    	while(R-L>=eps){
    		mid=(L+R)/2;
    		if(count()>=k) L=mid;
    		else R=mid;
    	}
    	
    	for(int i=1;i<=cnt;i++) c[i]%=ha;
    	for(int i=cnt;i;i--) hz[i]=add(hz[i+1],c[i]);
    	
    	ll N=0;
    	int cj=0,l=1;
    	
    	for(int i=cnt;i;i--){
    		while(l<cnt&&b[l]+b[i]+eps<L) l++;
    		
    		if(b[l]+b[i]+eps<L) break;
    		else if(fabs(b[l]+b[i]-L)<=eps) cj=c[i]*(ll)c[l]%ha;
    		
    		N+=(ll)(cnt-l+1);
    		ADD(ans,c[i]*(ll)hz[l]%ha);
    	}
    	
    	ADD(ans,ha-cj*(ll)(N-k)%ha);
    }
    
    inline void solve(){
    	dp();
    	calc();
    } 
    
    int main(){
    	scanf("%lld%d",&n,&k),init();
    	solve(),printf("%d
    ",ans);
    	
    	return 0;
    }
    

      

  • 相关阅读:
    Oracle去除重复(某一列的值重复),取最新(日期字段最新)的一条数据
    eclipse中的项目无法添加到tomcat中
    Myeclipse查看当前项目工作空间
    oracle查看表中否存在某字段,数据库是否存在某张表
    Java中Double原样输出,取消科学计数法
    DateTimeField *** received a naive datetime (***) while time zone support is active
    JS 将UTC时间转为本地时间
    Python与Django的时区问题
    在Django / DRF中正确处理日期时间/时区
    django时间的时区问题
  • 原文地址:https://www.cnblogs.com/JYYHH/p/9082363.html
Copyright © 2011-2022 走看看