题面传送门

题意:

给出 \(n,p\),求有多少 \(n\) 位数 \(X=a_1a_2a_3\dots a_n\) 满足:

  1. 该 \(n\) 位数不含前导零
  2. \(a_i \leq a_{i+1}\)
  3. \(X\) 为 \(p\) 的倍数。

答案对 \(998244353\) 取模。

\(1 \leq n \leq 10^{18}\),\(1 \leq p \leq 500\)。

CSP 之前做的了,隔了好久才把题解补了。。。

本题的突破口在于怎样处理 \(a_i \leq a_{i+1}\) 这个条件。

我们不妨进行一个转化:每次加一个全是 \(1\) 的后缀,最多可以加 \(9\) 次,这样就能保证得到的数一定满足条件 2。

又由于 \(X\) 不能含前导零,所以我们加的后缀中必须有一个是长度 \(n\) 的后缀。

于是我们有了优秀的 \(n^8\) 的做法,可以拿到 10 分的好成绩。

但仔细观察就可以发现,每个后缀 \(111...11\) 对 \(p\) 取模的余数呈周期分部。

例如当 \(p=12\) 的时候,各后缀模 \(p\) 的余数分别为:\(1,11,9,7,11,9,7,11,...\)

不难看出除了 \(1\) 以外每三个一循环。

有了这个发现,我们就可以将这 \(n\) 个后缀分为三类:

  1. 还没进循环节
  2. 在完整的循环节中
  3. 在最后多出的部分中

证明可用扩展欧拉定理。

记 \(cnt_m\) 为模 \(p\) 余 \(m\) 的后缀数。分类讨论可以在 \(\mathcal O(p)\) 的时间内求出 \(cnt\)。

接下来就可以 \(dp\) 了。\(dp[i][j][k]\) 表示选择了余数为 \(0\) 到 \(i\) 的后缀共 \(j\),它们的和模 \(p\) 余 \(k\) 的方案数。

转移的时候枚举选择多少个余数为 \(i\) 的后缀,记为 \(s\)。

由于可以重复选择,这一部分的方案数可以用隔板法求,\(\dbinom{cnt_i+s-1}{s}\)。

最后别忘了我们必须选择长度为 \(n\) 的后缀,所以答案为 \(\sum\limits_{i=0}^8dp[p-1][i][(p-pn)\mod m]\),其中 \(pn\) 为长度为 \(n\) 的后缀对 \(p\) 取模的结果。

别忘了特判 \(p=1\),卡了我很久。。。。。。

综上,这是一道非常不错的综合题。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define mp make_pair
#define int long long
typedef pair<int,int> pii;
typedef long long ll;
const ll MOD=999911659;
ll n;
int p,a[505];
int vis[505];
ll cnt[505];
ll dp[505][10][505];
inline ll qpow(ll x,ll e){
ll ans=1;
while(e){
if(e&1) ans=ans*x%MOD;
x=x*x%MOD;e>>=1;
}
return ans;
}
ll inv[505];
inline ll getc(ll x,ll y){
ll ans=1;
for(int i=x;i>=x-y+1;i--) ans=ans*(i%MOD)%MOD;
for(int i=1;i<=y;i++) ans=ans*inv[i]%MOD;
return ans;
}
inline void inc(ll &x,ll y){
x+=y;if(x>=MOD) x-=MOD;
}
signed main(){
scanf("%lld%lld",&n,&p);
for(int i=1;i<=10;i++) inv[i]=qpow(i,MOD-2);
int cur=1;vis[cur]=1%p;
int len1,len2;a[1]=1%p;
for(int i=2;i<=1000;i++){
cur=(cur*10+1)%p;a[i]=cur;
if(vis[cur]){
len1=vis[cur]-1;len2=i-vis[cur];
break;
}
vis[cur]=i;
}
if(n<=len1) for(int i=1;i<=n;i++) cnt[a[i]]++;
else{
ll cyc=(n-len1)/len2;
ll rem=(n-len1)%len2;
for(int i=1;i<=len1;i++) cnt[a[i]]++;
for(int i=len1+1;i<=len1+len2;i++) cnt[a[i]]+=cyc;
for(int i=len1+1;i<=len1+rem;i++) cnt[a[i]]++;
}
for(int i=0;i<9;i++){
dp[0][i][0]=getc(cnt[0]+i-1,i)%MOD;
}
for(int i=0;i<p-1;i++) for(int j=0;j<9;j++) for(int k=0;k<p;k++){
for(int l=0;l+j<9;l++){
inc(dp[i+1][l+j][(k+l*(i+1))%p],dp[i][j][k]*getc(cnt[i+1]+l-1,l)%MOD);
}
}
int gn;
if(n<=len1) gn=a[n];
else if((n-len1)%len2==0) gn=a[len1+len2];
else gn=a[len1+(n-len1)%len2];
ll ans=0;
for(int i=0;i<9;i++) inc(ans,dp[p-1][i][(p-gn)%p]);
printf("%lld\n",ans);
return 0;
}
/*
1000000000 499
1000000000000000000 1
2 1
*/

洛谷 P2481 [SDOI2010]代码拍卖会(背包+隔板法)的更多相关文章

  1. 洛谷 P2481 [SDOI2010]代码拍卖会

    洛谷 这大概是我真正意义上的第一道黑题吧! 自己想出了一个大概,状态转移方程打错了一点点,最后还是得看题解. 一句话题意:求出有多少个\(n\)位的数,满足各个位置上的数字从左到右不下降,且被\(p\ ...

  2. luogu P2481 [SDOI2010]代码拍卖会

    luogu 题目中的那个大数一定是若干个1+若干个2+若干个3...+若干个9组成的,显然可以转化成9个\(\underbrace {111...1}_{a_i个1}(0\le a_1\le a_2\ ...

  3. SDOI2010代码拍卖会 (计数类DP)

    P2481 SDOI2010代码拍卖会 $ solution: $ 这道题调了好久好久,久到都要放弃了.洛谷的第五个点是真的强,简简单单一个1,调了快4个小时! 这道题第一眼怎么都是数位DP,奈何数据 ...

  4. 洛谷 P2014 选课(树形背包)

    洛谷 P2014 选课(树形背包) 思路 题面:洛谷 P2014 如题这种有依赖性的任务可以用一棵树表示,因为一个儿子要访问到就必须先访问到父亲.然后,本来本题所有树是森林(没有共同祖先),但是题中的 ...

  5. 洛谷P4525 【模板】自适应辛普森法1与2

    洛谷P4525 [模板]自适应辛普森法1 与P4526[模板]自适应辛普森法2 P4525洛谷传送门 P4525题目描述 计算积分 结果保留至小数点后6位. 数据保证计算过程中分母不为0且积分能够收敛 ...

  6. BZOJ1925或洛谷2467 [SDOI2010]地精部落

    BZOJ原题链接 洛谷原题链接 先讲下关于波动数列的\(3\)个性质. 性质\(1\):对于数列中的每一对\(i\)和\(i + 1\),若它们不相邻,那么交换这两个数形成的依旧是一个波动数列. 性质 ...

  7. 洛谷 P4389: 付公主的背包

    题目传送门:洛谷 P4389. 题意简述: 有 \(n\) 个物品,每个物品都有无限多,第 \(i\) 个物品的体积为 \(v_i\)(\(v_i\le m\)). 问用这些物品恰好装满容量为 \(i ...

  8. 洛谷 P2467 [SDOI2010]地精部落

    洛谷 我讲的应该没有这个[https://www.luogu.org/blog/user55639/solution-p2467]清楚. 贴个代码算了: #include <bits/stdc+ ...

  9. 洛谷P2468 [SDOI2010]粟粟的书架

    来了来了,随便拽一道题写题解[大雾] 最近发现自己基础奇差于是开始复习之前学过的东西,正好主席树我几乎完全没学会,然后打开洛谷试炼场… 发现了这么一道二合一的题. 这道题其实分成两个部分,前50%是一 ...

随机推荐

  1. 初始HTML04

    HTML 列表标签 无序列表 默认用实心圆点标识列表项 1 <ul> 2 <li>list item 列表项</li> 3 <li>list item ...

  2. Java中的函数式编程(四)方法引用method reference

    写在前面 我们已经知道,lambda表达式是一个匿名函数,可以用lambda表达式来实现一个函数式接口.   很自然的,我们会想到类的方法也是函数,本质上和lambda表达式是一样的,那是否也可以用类 ...

  3. [对对子队]测试报告Beta

    一.测试中发现的bug BETA阶段的新bug 描述 提出者(可能需要发现者在会议上复现) 处理人 是否解决 第四关中工作区的循环语句拖动到组件区后成本的大小比原来不一样的问题 梁河览 何瑞 是 循环 ...

  4. Noip模拟32(再度翻车) 2021.8.7

    T1 Smooth 很水的一道题...可是最傻    的是考场上居然没有想到用优先队列优化... 上来开题看到这个,最一开始想,这题能用模拟短除法,再一想太慢了,就想着优化 偏偏想到线性筛然后试别的素 ...

  5. 常用JAVA API :String 、StringBuilder、StringBuffer的常用方法和区别

    摘要 本文将介绍String.StringBuilder类的常用方法. 在java中String类不可变的,创建一个String对象后不能更改它的值.所以如果需要对原字符串进行一些改动操作,就需要用S ...

  6. 6.深入TiDB:乐观事务

    本文基于 TiDB release-5.1进行分析,需要用到 Go 1.16以后的版本 我的博客地址:: https://www.luozhiyun.com/archives/620 事务模型概述 由 ...

  7. H3C 三层交换基于IP限速

    一.背景 目前百度爬虫爬取业务总是按照自己的性能进行抓取客户数据,从来不考虑客户端的网络承受能力,导致客户端网络带宽超出预算范围,因此在客户端方面针对百度的无限制抓取采取相应的策略. 二.解决方案: ...

  8. Python技法4:闭包

    闭包:用函数代替类 有时我们会定义只有一个方法(除了__init__()之外)的类,而这种类可以通过使用闭包(closure)来替代.闭包是被外层函数包围的内层函数,它能够获取外层函数范围中的变量(即 ...

  9. k8s入坑之路(10)kubernetes coredns详解

    概述 作为服务发现机制的基本功能,在集群内需要能够通过服务名对服务进行访问,那么就需要一个集群范围内的DNS服务来完成从服务名到ClusterIP的解析. DNS服务在kubernetes中经历了三个 ...

  10. 【Python+postman接口自动化测试】(5)抓包工具Fiddler简介

    Fiddler简介 Fiddler 4.6 下载 http://www.downza.cn/soft/234727.html 为什么使用Fiddler? 可以抓到请求数据,查看Raw格式/表单格式/J ...