[SDOI2010]代码拍卖会

题目描述

随着iPig在P++语言上的造诣日益提升,他形成了自己一套完整的代码库。猪王国想参加POI的童鞋们都争先恐后问iPig索要代码库。iPig不想把代码库给所有想要的小猪,只想给其中的一部分既关系好又肯出钱的小猪,于是他决定举行了一个超大型拍卖会。

在拍卖会上,所有的N头小猪将会按照和iPig的好感度从低到高,从左到右地在iPig面前站成一排。每个小猪身上都有9猪币(与人民币汇率不明),从最左边开始,每个小猪依次举起一块牌子,上面写上想付出的买代码库的猪币数量(1到9之间的一个整数)。大家都知道,如果自己付的钱比左边的猪少,肯定得不到梦寐以求的代码库,因此从第二只起,每只猪出的钱都大于等于左边猪出的价钱。最终出的钱最多的小猪(们)会得到iPig的代码库真传,向着保送PKU(Pig Kingdom University)的梦想前进。

iPig对自己想到的这个点子感到十分满意,在去现场的路上,iPig就在想象拍卖会上会出现的场景,例如一共会出现多少种出价情况之类的问题,但这些问题都太简单了,iPig早已不敢兴趣了,他想要去研究更加困难的问题。iPig发现如果他从台上往下看,所有小猪举的牌子从左到右将会正好构成一个N位的整数,他现在想要挑战的问题是所有可能构成的整数中能正好被P整除的有多少个。由于答案过大,他只想要知道答案mod 999911659就行了。

输入输出格式

输入格式:

 

输入文件auction.in有且仅有一行:两个数N(1≤N≤10^18)、P(1≤P≤500),用一个空格分开。

 

输出格式:

 

输入文件auction.out有且仅有一行:一个数,表示答案除以999911659的余数。

 

输入输出样例

输入样例#1: 
2 3
输出样例#1: 
15

说明

样例解释

方案可以是:12 15 18 24 27 33 36 39 45 48 57 66 69 78 99,共15种。

数据规模

 

    这个题一不小心就往矩阵乘法上想了,,,,

    但是矩乘肯定过不去啊。。。

 

    恰好N位不太好处理,所以我们考虑一下<=N位的所有满足条件的数怎么求。

    因为最大的位<=9,且后面的位都>=前面的位,所以我们考虑从 1,11,111,1111.....,1...1(n个1),中选不超过9个数(不能不选,且一个数可以选多次),使得他们的和%P为0的方案数。

    可以发现这样的方案数就是<=N位的数中满足条件的数的个数。

 

    但现在有一个问题:那些数有n(<=10^18)个,太多了。

    但是我们发现,选一个数对总和%P造成的影响只和 这个数%P的值有关,而P<=500,所以我们就可以把这些数分成不超过500个等价类,再记一下每个等价类中数的数量就行了。

    直接找肯定是不行的,但我们发现11..1(i个1)=11..1(i-1个1)*10 + 1 (这不是废话嘛333),也就是每个数在同余系下只有一条出边,这样我们就直接找环好了。

 

    然后就是简单的dp过程了,懒得讲了(别忘了最后用N位的答案-N-1位的答案)。

 

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int ha=999911659;
ll N;
int P,num[505],now,ans,C[505][12]; 
int f[505][505][12],sum[505],inv[12];
bool v[505],flag=0;

inline int add(int x,int y,const int mod){
	x+=y;
	return x>=mod?x-mod:x;
}

inline void prework(){
	int tag,len=0;
	
	num[now=1]=1;
	while(!v[num[now]]){
		v[num[now]]=1;
		num[now+1]=(num[now]*10+1)%P;
		now++;
	}
	
	now--;
	for(int i=1;i<=now;i++) if(num[i]==num[now+1]){
		tag=i;
		break;
	}
	
	for(int i=1;i<tag;i++) if(N>=i) sum[i]=1;
	len=now-tag+1;
	for(int i=tag;i<=now;i++) if(N>=i) sum[i]=((N-i)/len+1)%ha;
	
	inv[1]=1;
	for(int i=2;i<=10;i++) inv[i]=-inv[ha%i]*(ll)(ha/i)%ha+ha;
	
	for(int i=1,T,S,tmp;i<=now;i++){
		C[i][0]=1;
		T=sum[i],S=tmp=1;
		for(int j=1;j<=9;j++,T=add(T,1,ha)){
			S=S*(ll)inv[j]%ha;
			tmp=tmp*(ll)T%ha;
			C[i][j]=tmp*(ll)S%ha;
		}
	}
}

inline void dp(){
	f[0][0][0]=1;
	for(int i=0;i<now;i++){
		if(!sum[i+1]){
			now=i;
			break;
		}
	    for(int j=0;j<P;j++)
	        for(int k=0,O,U;k<=9;k++) if(f[i][j][k]){
	        	O=f[i][j][k],U=j;
	        	for(int l=0;l+k<=9;l++,U=add(U,num[i+1],P)){
	        		f[i+1][U][l+k]=add(f[i+1][U][l+k],O*(ll)C[i+1][l]%ha,ha);
				}
			}
    }
    
}

inline void calc(){
	for(int i=1;i<=9;i++) 
	    if(!flag) ans=add(ans,f[now][0][i],ha);
	    else ans=add(ans,ha-f[now][0][i],ha);
}

inline void init(){
	memset(v,0,sizeof(v));
	memset(f,0,sizeof(f));
	memset(sum,0,sizeof(sum));
}

int main(){
	scanf("%lld%d",&N,&P);
	prework();
	dp();
	calc();
	
	init(),N--,flag=1;
	prework();
	dp();
	calc();
	
	printf("%d\n",ans);
	return 0;
}

  

posted @ 2018-04-02 21:58  蒟蒻JHY  阅读(308)  评论(0编辑  收藏  举报