BZOJ 1974 [Sdoi2010] auction 代码拍卖会

216 篇文章 0 订阅
160 篇文章 0 订阅

Description

随着iPig在P++语言上的造诣日益提升,他形成了自己一套完整的代
码库。猪王国想参加POI的童鞋们都争先恐后问iPig索要代码库。iPi
g不想把代码库给所有想要的小猪,只想给其中的一部分既关系好又
肯出钱的小猪,于是他决定举行了一个超大型拍卖会。 在拍卖会上
,所有的N头小猪将会按照和iPig的好感度从低到高,从左到右地在i
Pig面前站成一排。每个小猪身上都有9猪币(与人民币汇率不明),
从最左边开始,每个小猪依次举起一块牌子,上面写上想付出的买代
码库的猪币数量(1到9之间的一个整数)。大家都知道,如果自己付
的钱比左边的猪少,肯定得不到梦寐以求的代码库,因此从第二只起
,每只猪出的钱都大于等于左边猪出的价钱。最终出的钱最多的小猪
(们)会得到iPig的代码库真传,向着保送PKU(Pig Kingdom Unive
rsity)的梦想前进。 iPig对自己想到的这个点子感到十分满意,在
去现场的路上,iPig就在想象拍卖会上会出现的场景,例如一共会出
现多少种出价情况之类的问题,但这些问题都太简单了,iPig早已不
敢兴趣了,他想要去研究更加困难的问题。iPig发现如果他从台上往
下看,所有小猪举的牌子从左到右将会正好构成一个N位的整数,他
现在想要挑战的问题是所有可能构成的整数中能正好被P整除的有多
少个。由于答案过大,他只想要知道答案mod 999911659就行了。

Input

一行:两个数N(1≤N≤10^18)、P(1≤P≤500),用一个空格分开。

Output

一行:一个数,表示答案除以999911659的余数。

Sample Input

2 3

Sample Output

15
样例解释
方案可以是:12 15 18 24 27 33 36 39 45 48 57 66 69 78 99,共15种。
数据规模
测试点 N P 测试点 N P
1 ≤1000 ≤500 6 ≤10^6 ≤500
2 ≤10^18 5 7 ≤10^18 ≤120
3 ≤10^18 ≤10 8 ≤10^18 ≤500
4 ≤10^18 ≤10 9 ≤10^18 ≤500
5 ≤10^18 25 10 ≤10^18 ≤500

HINT

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

神奇的思路+DP~

在考场上A掉这题的神犇实在是太强了……

因为数从左往右不下降,所以每个数都可以表示为0,1,11,111,1111,…,111…111这些数中的9个之和(可以重复),而这些数%p一定会有循环,且循环节小于等于p,所以我们用cnt[i]记录模p余数是i的111…111的数量,然后就是DP了。

DP方法:

用f[i][j][k]表示枚举到cnt[i],目前%p余数是j,已经用了k个形为111…111的数,但是注意到这样空间复杂度可能无法承受,而i对以后的递推并无影响,所以我们把i这一维换成滚动的,变成f[kkz][j][k]。

所以更新方法就是f[kkz][(j+i*z)%p][k+z]=(f[kkz][(j+i*z)%p][k+z]+1ll*f[kkz^1][j][k]*c[i][z])%modd,其中c[i][z]表示从cnt[i]中选z个的种类数(可重复),枚举z即可。

几个注意点:

(1)要乘1ll(1LL同义);

(2)cnt[i]要开long long;

(3)位运算要加括号!位运算要加括号!位运算要加括号!

(4)k==0 ? tot=i:1和tot=k==0 ? i:1是有很大差别的.


#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long
#define modd 999911659

int p,f[2][500][10],i,j,k,z,tot,b[501],c[501][9],len,kkz,ans,inv[501];
ll n,cnt[501];

int getc(ll u,int v)
{
	int totn=inv[v];
	for(u%=modd;v--;totn=1ll*totn*u%modd,u--);
	return totn;
}

int main()
{
	scanf("%lld%d",&n,&p);
	if(n<=p)
	{
		for(i=j=1;j<n;j++,i=(i*10+1)%p) cnt[i]++;cnt[tot=i]++;
	} 
	else
	{
		for(b[i=1%p]=len=1;!b[i=(i*10+1)%p];b[i]=++len);
		k=len;len-=b[i]-1;
		for(i=1%p,j=1;j<=k;i=(i*10+1)%p,j++) cnt[i]=1;
		for(;j<=n && j<=k+len;j++,i=(i*10+1)%p)
		{
			cnt[i]+=(n-j+1)/len+((n-j+1)%len!=0);
			(n-j)%len==0 ? tot=i:1;
		}
	}
	tot=(p-tot)%p;
	for(inv[0]=inv[1]=1,i=2;i<=15;i++) inv[i]=1ll*(modd-modd/i)*inv[modd%i]%modd;
	for(i=2;i<=15;i++) inv[i]=1ll*inv[i]*inv[i-1]%modd;
	for(i=0;i<p;i++)
	  if(cnt[i])
	    for(j=0;j<9;j++) c[i][j]=getc(cnt[i]+j-1,j);
	f[0][0][0]=1;
	if(cnt[0]) for(i=1;i<9;i++) f[0][0][i]=c[0][i];
	for(i=1;i<p;i++)
	  if(cnt[i])
	  {
		kkz^=1;
		memset(f[kkz],0,sizeof(f[kkz]));
		for(j=0;j<p;j++)
		  for(k=0;k<9;k++)
		    if(f[kkz^1][j][k])
		      for(z=0;k+z<9;z++)
		        f[kkz][(j+i*z)%p][k+z]=(f[kkz][(j+i*z)%p][k+z]+1ll*f[kkz^1][j][k]*c[i][z])%modd;
	  }
	for(int i=0;i<9;i++) ans=(ans+f[kkz][tot][i])%modd;
	printf("%d\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值