题解 P2481 【[SDOI2010]代码拍卖会】
题意转化
本题的转化非常妙妙!
对于任何一个猪猪举牌的方案,都可以看做
感性理解如下图:
于是我们把一个方案拆成了若干的形如
题目中让我们求
想想怎么做?
我们可以把一个数分割成若干个
所以我们可以计算出
因为可以选至多
然而这一个点都过不去啊
其实我们可以直接把
比如
不妨记
(最后再讲
我们就可以不用暴力
而是做一个背包的转移就好了!
背包
设
枚举“
枚举当前已经铺了几个“
为什么是
枚举当前这种 “
枚举转移过来的状态
则转移方程是:
这个
就是在
证明即隔板法。
复杂度即
至于怎么计算这个组合数,直接按定义计算就好。
循环节的计算
那么怎么计算
普及组知识?
定义
如
则显然有
显然这柿子在
可以把
-
未进入循环段
-
循环段
-
不完整循环段
暴力统计 未进入和不完整 的,用循环节搞定循环的就好了。
代码如下:
#include <cstring>
#include <iostream>
using namespace std;
#define mod (999911659LL)
#define MX (500 + 5)
#define LL long long
LL qpow(LL x ,LL y ,LL P){
if(!y) return 1;
if(y == 1) return x;
LL t = qpow(x ,y >> 1 ,P);
if(y & 1) return t * t % P * x % P;
return t * t % P;
}
LL g[MX] ,n ,p ,cycLen ,cycstNum;
int cycle[MX * MX] ,first[MX];
LL dp[MX][11][MX];
LL C(LL n ,LL m){
if(m < 0) return 0;
LL fz = 1 ,fm = 1;
for(int i = 0 ; i < m; ++i){
(fz *= n - i) %= mod;
(fm *= m - i) %= mod;
}
return fz * qpow(fm ,mod - 2 ,mod) % mod;
}
int main(){
// cout << C(10 ,7);
memset(first ,-1 ,sizeof first);
cin >> n >> p;
cycle[0] = 1 % p;
first[1 % p] = 0;
LL addition = 0;
// first 是该数第一次出现的位置
// cycle 是按顺序出现的数 %p
for(int i = 1 ; ; ++i){
cycle[i] = (cycle[i - 1] * 10 + 1) % p;
if(~first[cycle[i]]){
for(int j = 0 ; j < i && j < n; ++j)
g[cycle[j]]++ ,addition = cycle[j];
n -= i;
cycstNum = cycle[i];
cycLen = i - first[cycle[i]];
break;
}
first[cycle[i]] = i;
}
n = max(n ,0LL);
LL times = n / cycLen;
for(int i = 0 ; i < cycLen ; ++i)
g[cycle[i + first[cycstNum]]] += times;
LL nn = n % cycLen;
for(int i = 0 ; i < nn ; ++i)
g[cycle[i + first[cycstNum]]]++ ,addition = cycle[i + first[cycstNum]];
for(int i = 0 ; i < p ; ++i) g[i] %= mod;
// 因为我很懒,按照上文博客
// 在计算 dp[0][][] 的时候会调用 dp[-1][][] 导致 RE
// 所以我是倒着来的,把 dp[0] 变成 dp[p] ,dp[1] 变成 dp[p - 1]...
dp[p + 1][0][addition] = 1;
// 此处 addition 是强制要求选择一次全 111111... 造成的代价
for(int i = 0 ; i < p ; ++i){ // 枚举使用的 0000...1111 %p 意义的值
for(int j = 0 ; j < 9 ; ++j){ // 枚举已经使用次数
for(int s = 0 ; s + j < 9 ; ++s){ // 枚举当前使用次数
LL multi = C(g[i] + s - 1 ,s);
for(int d = 0 ; d < p ; ++d){ // 枚举已经的余数
// if(dp[p - i + 1][j][d]){printf("dp[%lld][%d][%lld] += multi(%lld) * dp[%lld][%d][%d](%lld);\n" ,p - i ,s + j ,(d + s * i) % p ,multi ,p - i + 1 ,j ,d ,dp[p - i + 1][j][d]);}
(dp[p - i][s + j][(d + s * i) % p] += multi * dp[p - i + 1][j][d]) %= mod;
}
}
}
}
LL Ans = 0;
for(int i = 0 ; i < 9 ; ++i){
(Ans += dp[1][i][0]) %= mod;
}
cout << Ans << endl;
return 0;
}
```