Topcoder10566 IncreasingNumber
IncreasingNumber
一个数是Increasing当且仅当它的十进制表示是不降的,\(1123579\)。
求 \(n\) 位不降十进制数中被 \(d\) 整除的有多少个。
\(n\leq 10^{18},d \leq 500\)
题解
简单的想法:\(dp(i,j,k)\) 表示前 \(i\) 位已填好,第 \(i\) 位是 \(j\),模 \(d=k\) 的数的个数。
但是即使加上矩阵优化,复杂度仍然达到了 \(O(10^3d^3 \log n)\)。不可过。
观察性质:一个数是Increasing的当且仅当它是至多 \(9\) 个全 \(1\) 的数的和。
由于最终产生的数必须严格 \(n\) 位,不能有前导 \(0\),所以我们对位数 \(< n\) 的全 \(1\) 数和 \(=n\) 的全 \(1\) 数分开做。
我们可以用 \(dp(k,j,mod)\) 表示考虑了 \(\bmod d=0\sim k\) 的全 \(1\) 数,用了 \(j\) 个数,余数是 \(mod\) 的方案数。
转移系数是 \(\binom{j+cnt_k-1}{cnt_k-1}\),其中 \(cnt_k\) 表示 \(1\sim n-1\) 位的全 \(1\) 数中,\(\bmod d=k\) 的数的个数。开始我想的是 \(cnt_k^j\),后来发现这样就相当于有标号了。
考虑 \(n\) 位全 \(1\) 数模 \(d\) 的余数和 \(cnt\) 数组如何求。可以利用 \(d\) 比较小来找循环节。注意循环节和起始点可能类似符号 \(\rho\) ,要加上对 \(n\) 的特判。
AC程序:
#include<bits/stdc++.h>
using namespace std;
template<class T> T read(){
T x=0,w=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*w;
}
template<class T> T read(T&x){
return x=read<T>();
}
#define CO const
#define IN inline
typedef long long LL;
CO int mod=1000000000+7;
IN int add(int a,int b){
return (a+=b)>=mod?a-mod:a;
}
IN int mul(int a,int b){
return (LL)a*b%mod;
}
IN int fpow(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a))
if(b&1) ans=mul(ans,a);
return ans;
}
IN void upd(int&a,int b){
a=add(a,b);
}
CO int N=500+10;
int pw[N],pst,pcir;
int bas[N],bst,bcir;
LL cnt[N];
int dp[N][10][N];
CO int inv[10]={1,1,500000004,333333336,250000002,400000003,166666668,142857144,125000001,111111112,};
IN int binom(LL n,int m){
if(m==0) return 1;
if(n<m) return 0;
int ans=1;
for(int i=1;i<=m;++i) ans=mul(ans,mul((n-i+1)%mod,inv[i]));
return ans;
}
struct IncreasingNumber{
static int countNumbers(LL n,int d){
--n;
pw[0]=1%d;
for(int i=1;i<=d;++i){
pw[i]=pw[i-1]*10%d;
for(int j=0;j<i;++j)
if(pw[j]==pw[i]){
pst=j,pcir=i-j;
break;
}
if(pcir) break;
}
int rn=0;
if(n<pst){
for(int i=0;i<=n;++i) rn=(rn+pw[i])%d;
}
else{
for(int i=0;i<pst;++i) rn=(rn+pw[i])%d;
int sum=0;
for(int i=1;i<=pcir;++i) sum=(sum+pw[pst-1+i])%d;
rn=(rn+(n-pst+1)/pcir%d*sum)%d;
for(int i=1;i<=(n-pst+1)%pcir;++i) rn=(rn+pw[pst-1+i])%d;
}
// cerr<<"rn="<<rn<<endl;
bas[1]=1%d;
for(int i=2;i<=d+1;++i){
bas[i]=(10*bas[i-1]+1)%d;
for(int j=1;j<i;++j)
if(bas[j]==bas[i]){
bst=j,bcir=i-j;
break;
}
if(bcir) break;
}
if(n<bst){
for(int i=1;i<=n;++i) ++cnt[bas[i]];
}
else{
for(int i=1;i<bst;++i) ++cnt[bas[i]];
for(int i=1;i<=bcir;++i) cnt[bas[bst-1+i]]+=(n-bst+1)/bcir;
for(int i=1;i<=(n-bst+1)%bcir;++i) ++cnt[bas[bst-1+i]];
}
// cerr<<"cnt=";
// for(int k=0;k<d;++k)if(cnt[k])
// cerr<<" ("<<k<<","<<cnt[k]<<")";
// cerr<<endl;
for(int j=0;j<=9;++j)
dp[0][j][0]=binom(j+cnt[0]-1,j);
for(int k=0;k<d;++k)for(int j=0;j<=9;++j)
for(int r=0;r<d;++r)if(dp[k][j][r])
for(int j1=0;j1<=9-j;++j1)
upd(dp[k+1][j+j1][(r+j1*(k+1))%d],mul(binom(j1+cnt[k+1]-1,j1),dp[k][j][r]));
int ans=0;
for(int j=0;j<=8;++j)
for(int j1=1;j1<=9-j;++j1)
upd(ans,dp[d-1][j][(d-j1*rn%d)%d]);
return ans;
}
};
//int main(){
// LL n=read<LL>();
// int d=read<int>();
// int ans=IncreasingNumber::countNumbers(n,d);
// printf("%d\n",ans);
//}
话说Topcoder让你实现一个类,我没看出这样做有什么好处。Z前辈告诉我这样大概不用消除了读入时间的影响?
开始我的程序漏洞百出,最后写了个对拍才调出来。
暴力DP程序:
CO int N=500+10;
int f[N][N][N];
int main(){
int n=read<int>(),d=read<int>();
f[0][1][0]=1;
for(int i=0;i<n;++i)for(int j=1;j<=9;++j)
for(int k=0;k<d;++k)if(f[i][j][k])
for(int j1=j;j1<=9;++j1) upd(f[i+1][j1][(10*k+j1)%d],f[i][j][k]);
int ans=0;
for(int j=1;j<=9;++j) upd(ans,f[n][j][0]);
printf("%d\n",ans);
return 0;
}
对拍程序:
int main(){
for(int i=3;i<=18;++i)
for(int j=1;j<=500;++j){
cerr<<"T "<<i<<" "<<j<<endl;
FILE*f=fopen("std.in","w");
fprintf(f,"%d %d\n",i,j);
fflush(f);
system("std.exe < std.in > std.out");
system("test.exe < std.in > test.out");
if(system("fc std.out test.out")) return 1;
}
return 0;
}
我发现如果直接freopen的话system的调用会失效。
注意那个fflush。我发现如果把造数据和对拍写在一起,就是又有输出又有调用其他程序的话,在输出和调用之间会卡住。这时候需要加个cerr或者fflush刷新一下。
Topcoder10566 IncreasingNumber的更多相关文章
随机推荐
- 【LOJ2292】[THUSC2016]成绩单(区间DP)
题目 LOJ2292 分析 比较神奇的一个区间 DP ,我看了很多题解都没看懂,大约是我比较菜罢. 先明确一下题意:abcde 取完 c 后变成 abde ,可以取 bd 这样取 c 后新增的连续段. ...
- td宽度自适应 窄的地方自动收缩
.layui-table td,.layui-table th { white-space: nowrap; width: auto; min-width: 0px!important; } 不换行! ...
- Linux下将.Asp Core 部署到 Docker容器中
我们来部署一个简单的例子: 将一个简单的.Aps Core项目部署到Docker容器中并被外网访问 说明: 下面的步骤都是建立在宿主服务器系统已经安装配置过Docker容器,安装Docker相对比较简 ...
- [转帖]记一次KUBERNETES/DOCKER网络排障
记一次KUBERNETES/DOCKER网络排障 https://coolshell.cn/articles/18654.html 记得之前在一个公众号里面看过这个文章 讲的挺好的.. 物理机直接跑d ...
- JQuery高级(二)
3. 事件绑定 1. jquery标准的绑定方式 * jq对象.事件方法(回调函数): * 注:如果调用事件方法,不传递回调函数,则会触发浏览器默认行为. * 表单对象.submit();//让表单提 ...
- Python之路【第十七篇】:Python并发编程|协程
一.协程 协程,又叫微线程,纤程.英文名Coroutine.协程本质上就是一个线程 优点1:协程极高的执行效率.因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线 ...
- Locust性能测试_先登录场景案例
前言 有很多网站不登录的话,是无法访问到里面的页面的,这就需要先登录了实现场景:先登录(只登录一次),然后访问页面->我的地盘页->产品页->项目页 官方案例 下面是一个简单的loc ...
- WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片
原文:WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片 我们知道,在 WPF 中的坐标单位不是屏幕像素单位,所以如果需要知道某个控件的像素尺寸,以便做一些与屏幕像素尺寸相关 ...
- 定时任务-Windows任务
定时任务-Windows任务 什么是windows任务 windows系统自带一个任务管理组件.可以执行自己写的程序,发送电子邮件(需要邮件服务器),显示消息(就是桌面弹出一个窗口).用的最多的就 ...
- dump net core windbg 安装
安装 1.下载工具windbg 地址:https://www.microsoft.com/zh-cn/p/windbg-preview/9pgjgd53tn86?SilentAuth=1&rt ...