HDU2089
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
char ch=' ';int f=1;int x=0;
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int dp[20][2];
int a[20];
int dfs(int pos,int pre,int state,bool limit)
{
if(pos==0) return 1.;
if(!limit&&dp[pos][state]!=-1) return dp[pos][state];
int up=limit?a[pos]:9;
int ans=0;
for(int i=0;i<=up;i++)
{
if(i==4) continue;
if(pre==6&&i==2) continue;
ans+=dfs(pos-1,i,i==6,limit&&i==a[pos]);
}
if(!limit) dp[pos][state]=ans;
return ans;
}
int solve(int x)
{
int pos=0;
while(x)
{
a[++pos]=x%10;
x/=10;
}
return dfs(pos,0,0,1);
}
int main()
{
int l,r;
while(scanf("%d%d",&l,&r)!=EOF)
{
if(l==0&&r==0)
{
break;
}
memset(dp,-1,sizeof(dp));
printf("%d\n",solve(r)-solve(l-1));
}
return 0;
}
HDU 3652
统计区间 [1,n] 中含有 ‘13’ 且模 13 为 0 的数字有多少个。
◦N<=10^9
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int f[10][2][2][20];
// f[pos][pre][have][k]
int a[10];
int p[12];
int dfs(int pos,bool pre,bool have,int k,bool limit)
{
if(pos==-1&&k==0&&have) return 1;
if(pos==-1) return 0;
if(!limit&&f[pos][pre][have][k]!=-1) return f[pos][pre][have][k];
int up=limit?a[pos]:9;int ans=0;
for(int i=0;i<=up;i++)
{
int h=(k+i*p[pos])%13;
ans+=dfs(pos-1,i==1,have||(pre&&i==3),h,limit&&i==a[pos]);
}
if(!limit) f[pos][pre][have][k]=ans;
return ans;
}
int solve(int n)
{
int pos=0;
while(n)
{
a[pos++]=n%10;
n=n/10;
}
return dfs(pos-1,0,0,0,true);
}
int main()
{
int n;
p[0]=1;
for(int i=1;i<=10;i++) p[i]=p[i-1]*10;
while(scanf("%d",&n)!=EOF)
{
memset(f,-1,sizeof(f));
cout<<solve(n)<<endl;
}
return 0;
}
P2657 [SCOI2009]windy数
题目描述
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int a[12];
int f[12][12];
int dfs(int pos,int pre,bool limit,bool lead)
{
if(pos==-1) return 1;
if(!limit&&!lead&&f[pos][pre]!=-1) return f[pos][pre];
int up=limit?a[pos]:9;int ans=0;
if(!lead)
for(int i=0;i<=up;i++)
{
if(abs(i-pre)<2) continue;
ans+=dfs(pos-1,i,limit&&i==up,lead&&(i==0));
}
else
for(int i=0;i<=up;i++)
{
ans+=dfs(pos-1,i,limit&&i==up,lead&&(i==0));
}
if(!limit&&!lead) f[pos][pre]=ans;
return ans;
}
int solve(int n)
{
int pos=0;
while(n)
{
a[pos++]=n%10;
n=n/10;
}
memset(f,-1,sizeof(f));
return dfs(pos-1,0,true,true);
}
int main()
{
int a,b;
cin>>a>>b;
cout<<solve(b)-solve(a-1)<<endl;
return 0;
}
P4124 [CQOI2016]手机号码
题目描述
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。
工具需要检测的号码特征有两个:号码中要出现至少 33 个相邻的相同数字;号码中不能同时出现 88 和 44。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是 1111 位数,前不含前导的 00。工具接收两个数 LL 和 RR,自动统计出 [L,R][L,R] 区间内所有满足条件的号码数量。LL 和 RR 也是 1111 位的手机号码。
输入格式
输入文件内容只有一行,为空格分隔的 22 个正整数 L,RL,R。
输出格式
输出文件内容只有一行,为 11 个整数,表示满足条件的手机号数量。
输入输出样例
输入 #1
12121284000 12121285550
输出 #1
5
说明/提示
样例解释:满足条件的号码: 12121285000、 12121285111、 12121285222、 12121285333、 12121285550。
数据范围: 1 0 10 ≤ L ≤ R < 1 0 11 10^{10}\leq L\leq R<10^{11} 1010≤L≤R<1011
思路
- dp 要求记录详细,所以需要记录
is8 -> 8是否出现
is4->4是否出现
same 前前和前是否相等
pre 想一个具体是啥
have 之前有没有三个相等
limit
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ff f[pos][is8][is4][pre][same][have]
#define ll long long
using namespace std;
ll a[15];
ll f[15][2][2][10][2][2];
// pos
ll dfs(int pos,bool limit,bool is8,bool is4,int pre,bool same,bool have)
{
if(pos==-1&&have) return 1;
if(pos==-1) return 0;
if(!limit&&ff!=-1) return ff;
int up=limit?a[pos]:9;ll ans=0;
for(int i=0;i<=up;i++)
{
if(is8&&i==4) continue;
if(is4&&i==8) continue;
ans+=dfs(pos-1,limit&&i==up,is8||i==8,is4||i==4,
i,i==pre,have||(same&&i==pre));
}
if(!limit) ff=ans;
return ans;
}
ll solve(ll n)
{
int pos=0;
while(n)
{
a[pos++]=n%10;
n=n/10;
}
memset(f,-1,sizeof(f));
return dfs(pos-1,true,false,false,-1,false,false);
}
int main()
{
ll n,m;
cin>>n>>m;
cout<<solve(m)-solve(n-1)<<endl;
return 0;
}
P4317 花神的数论题
设 sum(i) 表示 i 的二进制表示中 1 的个数。给出一个正整数 N ,花神要问
你派(Sum(i)),也就是 sum(1)—sum(N) 的乘积。答案对一个质数取模。
◦对于 100% 的数据,N≤10^15
思路
- 数位dp需要一个限制条件,而此题从1到N枚举,一是没有限制条件,二是复杂度
- 所以我们枚举二进制中1的个数 i,看看1~N里有多少个符合的数a,对ans的贡献就是i^a(ksm)
- 注意a不能mod
- 这个题相对于裸的数位dp就是外面套了一层枚举,加一个转换
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const int mod=10000007;
int a[70];
ll f[70][70];
ll ksm(ll x,ll y)
{
int ret=1;
while(y)
{
if(y&1) ret=(ll)ret*x%mod;
x=(ll)x*x%mod;
y=y>>1;
}
return ret;
}
ll dfs(int pos,int sum,bool limit)
{
if(pos==0&&sum==0) return 1;
if(pos==0) return 0;
if(sum>pos) return 0;
if(!limit&&f[pos][sum]!=-1) return f[pos][sum];
int up=limit?a[pos]:1;ll ans=0;
for(int i=0;i<=up;i++)
{
ans=ans+dfs(pos-1,sum-i,limit&&(i==up));
}
if(!limit) f[pos][sum]=ans;
return ans;
}
ll solve(ll n)
{
memset(f,-1,sizeof(f));
int pos=0;
while(n)
{
a[++pos]=n&1;
n=n>>1;
}
ll ans=1,k;
for(int i=1;i<=pos;i++)
{
k=dfs(pos,i,true);
if(k)ans=(ll)ans*ksm(i,k)%mod;
}
return ans;
}
int main()
{
ll n;cin>>n;
cout<<solve(n)<<endl;
return 0;
}
P2602 [ZJOI2010]数字计数
题目描述
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
输入格式
输入文件中仅包含一行两个整数a、b,含义如上所述。
输出格式
输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。
输入输出样例
输入 #1
1 99
输出 #1
9 20 20 20 20 20 20 20 20 20
说明/提示
30%的数据中,a<=b<=10^6;
100%的数据中,a<=b<=10^12。
思路
- 我们枚举0~9,对每一个数值分别统计。
- 注意前导零的处理
int a[20];
ll dp[20][20];
ll dfs(int pos,ll sum,int limit,int lead,int k)
{
if(pos==-1) return sum;
if(!limit&&!lead&&dp[pos][sum]!=-1) return dp[pos][sum];
int up=limit?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++)
{
ans+=dfs(pos-1,sum+((!lead||i)&&(i==k)),limit&&(i==up),lead&&(i==0),k);
}
if(!limit&&!lead) dp[pos][sum]=ans;
return ans;
}
ll solve(ll x,int k)
{
int pos=0;
while(x)
{
a[pos++]=x%10;
x=x/10;
}
memset(dp,-1,sizeof(dp));
return dfs(pos-1,0,1,1,k);
}
int main()
{
ll l,r;
l=read();r=read();
for(int i=0;i<=9;i++)
{
cout<<solve(r,i)-solve(l-1,i)<<' ';
}
return 0;
}