1.题目链接:codeforces 55D
【题意】一个数能被它每一位的数字整除(0除外)则为beautiful number ,问[l,r]之间有多少个这样的数
【思路】考虑到每一位的数字只能是1,2,,9,最小公倍数为2520
设数w=x*2520+y,每位数字的最小公倍数为mul,则w%mul=(x*2520+y)%mul=x*2520%mul+y%mul=y%mul,只需要记录每一步过后该数对2520取模即可。
dp[len][y][mul]表示处理到第len位时数对2520的模,及当前所有数字的最小公倍数。可开到dp[20][2520][2520]
由于数字的最小公倍数是有限的,可将第三维2520进一步优化,预处理求出2520所有的约数。
优化为dp[20][2520[60]
/***********************************************************************
> File Name: cf55d.cpp
> Author: wanghao
> Mail: haohaoac@163.com
> Created Time: 2015年07月06日 星期一 15时27分54秒
************************************************************************/
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
ll dp[20][2520][60];
int num[20];
int v[60],cnt;
int to[2600];
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
int lcm(int a,int b)
{
if(b==0)
return a;
return a*b/gcd(a,b);
}
ll dfs(int p,int y,int multi,int flag)
{
//cout<<p<<' '<<y<<' '<<multi<<' '<<flag<<endl;
if(p==-1)
return y%multi==0;
if(!flag&&dp[p][y][to[multi]]!=-1)
return dp[p][y][to[multi]] ;
ll res=0;
int u=flag?num[p]:9;
for(int i=0;i<=u;i++)
{
res+=dfs(p-1,(y*10+i)%2520,lcm(multi,i),flag&&i==num[p]);
}
if(!flag)
dp[p][y][to[multi]]=res;
//cout<<p<<' '<<y<<' '<<multi<<' '<<flag<<endl;
//cout<<res<<endl;
return res;
}
ll work(ll x)
{
int i=0;
while(x)
{
num[i++] =x%10;
x/=10;
}
return dfs(i-1,0,1,1) ;
}
int main()
{
cnt=0;
// memset(to,0,sizeof(to));
for(int i=1;i<=2520;i++)//优化第三维
{
if(2520%i==0)
{
v[cnt]=i;
to[i]=cnt++;
}
}
// cout<<cnt<<endl;
// for(int i=0;i<cnt;i++)
// {
// cout<<v[i]<<endl;
// }
int t;scanf("%d",&t);
memset(dp,-1,sizeof(dp));
while(t--)
{
ll a,b;
cin>>a>>b;
cout<<work(b)-work(a-1)<<'\n';
}
return 0;
}
2.题目链接:hdu4352
【题意】定义一个数的值为其数字最长上升子序列的长度,例如3746,最长上升序列为 3,4,6,值为3.求[l,r]个中值为k的数有多少个。
【思路】好题。。忍不住看了题解。。求最长上升子序列时,我们需要维护一个序列,表示长度为1的最后一个数的最小值,长度为2的最后一个数的最小值,长度为3的最后一个的最小值。。。这个序列是单调递增的,我们只需要记录下这个序列即可,只有0-9 10个数字,状态压缩!
/*************************************************************************
> File Name: hdu4352.cpp
> Author: wanghao
> Mail: haohaoac@163.com
> Created Time: 2015年07月09日 星期四 20时19分09秒
************************************************************************/
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
ll dp[20][1<<10][11];
int num[20];
int k;
int getlen(int state)
{
int res=0;
for(int i=0;i<10;i++)
res+=(state&(1<<i))!=0;
return res;
}
int update(int state,int v)
{
int len=getlen(state);
if(len==0&&v==0)return 0;
if(len==0)return 1<<v;
for(int i=9;i>=0;i--)
{
if(state&(1<<i))
if(i<v)
return state|(1<<v);
else break;
}
for(int i=0;i<=9;i++)
{
if(state&(1<<i))
{
if(i>=v)
return state^(1<<i)|(1<<v);
}
}
return state;
}
ll dfs(int p,int state,int len,int flag)
{
//cout<<p<<' '<<state<<' '<<len<<' '<<flag<<endl;
if(p==-1)
return getlen(state)==len;
if(!flag&&dp[p][state][len]!=-1)
return dp[p][state][len];
if(getlen(state)>len)return dp[p][state][len]=0;
ll res=0;
int u=flag?num[p]:9;
for(int i=0;i<=u;i++)
{
int now=update(state,i);
res+=dfs(p-1,now,len,flag&&i==num[p]);
}
// cout<<p<<' '<<state<<' '<<len<<' '<<flag<<endl;
// cout<<res<<endl;
if(!flag)
{
dp[p][state][len]=res;
}
return res;
}
ll work(ll x)
{
int i=0;
while(x)
{
num[i++]=x%10;
x/=10;
}
return dfs(i-1,0,k,1);
}
int main()
{
memset(dp,-1,sizeof(dp));
int t;
scanf("%d",&t);
int ca=0;
while(t--)
{
ll a,b;
scanf("%lld%lld%d",&a,&b,&k);
printf("Case #%d: ",++ca);
cout<<work(b)-work(a-1)<<endl;
}
return 0;
}
3.题目链接: hdu2089
【题意】含4或62的数为不吉利数,求[l,r]中有多少个吉利的数
【思路】水题。。直接写
<span style="font-size:14px;">/*************************************************************************
> File Name: hdu2089.cpp
> Author: wanghao
> Mail: haohaoac@163.com
> Created Time: 2015年07月06日 星期一 15时58分40秒
************************************************************************/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int num[10];
int len;
int dp[10][2];
int dfs(int p,int flag,int flag2)
{
if(p==-1)
return 1;
if(!flag&&dp[p][flag2]!=-1)
return dp[p][flag2];
int u=flag?num[p]:9;
int res=0;
for(int i=0;i<=u;i++)
{
if(i==2&&!flag2)continue;
if(i==4)continue;
res+=dfs(p-1,flag&&i==num[p],i!=6);
}
return dp[p][flag2]=res;
}
int work(int x)
{
memset(dp,-1,sizeof(dp));
int len=0;
while(x)
{
num[len++]=x%10;
x/=10;
}
return dfs(len-1,1,1);
}
int main()
{
int n,m;
while(cin>>n>>m)
{
if(n==0&&m==0)
break;
printf("%d\n",work(m)-work(n-1));
}
return 0;
}</span>
4.题目链接:hdu3555
【题意】跟上面一题差不多,求[1,x]中含49的有多少个
【思路】同样水题,记录前一个数字是不是4
<span style="font-size:14px;">/*************************************************************************
> File Name: hdu3555.cpp
> Author: wanghao
> Mail: haohaoac@163.com
> Created Time: 2015年07月06日 星期一 16时27分38秒
************************************************************************/
#include<iostream>
#include<cstring>
#include<cstdio>
#define ull unsigned long long
#define ll long long
using namespace std;
int num[25];
ll A[20];
long long dp[25][2];
long long dfs(int p,int flag,int flag4,int ok)
{
// cout<<p<<" "<<flag<<' '<<flag4<<' '<<ok<<endl;
if(p==-1)
return ok;
if(!flag&&ok)
{
// cout<<"in "<<A[p+1]<<endl;
return A[p+1];
}
if(!flag&&dp[p][flag4]!=-1)
return dp[p][flag4];
int u=flag?num[p]:9;
ll res=0;
for(int i=0;i<=u;i++)
{
res+=dfs(p-1,flag&&i==num[p],i==4,ok||(flag4&&i==9));
}
return dp[p][flag4] = res;
}
ull work(ull x)
{
memset(dp,-1,sizeof(dp));
int i=0;
while(x)
{
num[i++]=x%10;
x/=10;
}
return dfs(i-1,1,0,0);
}
int main()
{
int t;
A[0]=1;
for(int i=1;i<=18;i++)
{
A[i]=A[i-1]*10;
// cout<<A[i]<<' ';
}
scanf("%d",&t);
while(t--)
{
unsigned long long a;
scanf("%I64u",&a);
printf("%I64u\n",work(a));
//cout<<work(a)<<endl;
}
return 0;
}</span>
5.题目链接:poj3252
【题意】将数化为二进制,0的个数不比1少,求[l,r]中这样的数有多少
【思路】将数转化为2进制,再进行dp,保存当前0,1的个数,注意开头取0的情况
<span style="font-size:14px;">/*************************************************************************
> File Name: poj3252.cpp
> Author: wanghao
> Mail: haohaoac@163.com
> Created Time: 2015年07月06日 星期一 19时23分03秒
************************************************************************/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int dp[35][35][35][2];
int num[35];
int dfs(int p,int num0,int num1,int flag)
{
// cout<<p<<' '<<num0<<' '<<num1<<' '<<flag<<endl;
if(p==-1)
return num0>=num1;
if(dp[p][num0][num1][flag]!=-1)
return dp[p][num0][num1][flag];
int u=flag?num[p]:1;
int res=0;
for(int i=0;i<=u;i++)
{
res+=dfs(p-1,num1?num0+!i:0,num1+i,flag&&i==num[p]);
}
// cout<<"ans "<<endl;
// cout<<p<<' '<<num0<<' '<<num1<<' '<<flag<<endl;
// cout<<res<<endl;
return dp[p][num0][num1][flag]=res;
}
int work(int x)
{
int i=0;
while(x)
{
num[i++]=x%2;
x/=2;
}
// for(int j=i-1;j>=0;j--)
// cout<<num[j];
// cout<<endl;
memset(dp,-1,sizeof(dp));
return dfs(i-1,0,0,1);
}
int main()
{
int a,b;
while(scanf("%d%d",&a,&b)!=EOF)
{
printf("%d\n",work(b)-work(a-1));
}
return 0;
}</span>
6.题目链接:hdu3709
【题意】取一个数中的某一数字为支点,左右距离*数字之和相等即为平衡数,例如4139,取3为支点,4*2+1*1=9*1,4139为平衡数求[l,r]间有多少个平衡数
【思路】对于一个数,若其为平衡数,则平衡点必唯一。设取x 点为支点,左侧加权和为Lx,数字和为SUMl;右侧加权和为Rx,数字和为SUMr.该数字为N x,则支点右移一位左侧加权和为Lx+SUMl+Nx,右侧加权和为Rx-SUMr。
观察原左右侧加权和之差,Lx-Rx,现加权和之差,Lx-Rx-(SUMl+Nx+SUMr)=Lx-Rx-sum(所有数字之和)。
发现,对于每一个支点,求得的差都是不一样的,因此平衡点必唯一。若存在平衡点,则Lx-Rx=0,取任意一点为支点,差值必然是sum的倍数!若差值为sum的倍数则必然存在平衡点,最左侧为支点差值<=0,最右侧为支点差值>=0,中间必有平衡点!以最右侧为支点,算出的差值为sum的倍数则该数是平衡数
<span style="font-size:14px;">/*************************************************************************
> File Name: hdu3709.cpp
> Author: wanghao
> Mail: haohaoac@163.com
> Created Time: 2015年07月06日 星期一 20时07分21秒
************************************************************************/
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
int num[25];
ll dp[2000][180][20];
ll dfs(int p,int value,int sum,int flag)
{
//cout<<p<<' '<<value<<' '<<sum<<' '<<flag<<endl;
if(p==-1)
return sum==0||value%sum==0;
if(!flag&&dp[value][sum][p]!=-1)
return dp[value][sum][p];
int u=flag?num[p]:9;
ll res=0;
for(int i=0;i<=u;i++)
{
res+=dfs(p-1,value+i*(p+1),sum+i,flag&&i==num[p]);
}
if(!flag)
dp[value][sum][p]=res;
// cout<<p<<' '<<value<<' '<<sum<<' '<<flag<<endl;
// cout<<res<<endl;
return res;
}
long long work(ll x)
{
// cout<<"work "<<endl;
// cout<<x<<endl;
if(x==-1)
return 0;
int i=0;
while(x)
{
num[i++]=x%10;;
x/=10;
}
return dfs(i-1,0,0,1);
}
int main()
{
// freopen("a.txt","r",stdin);
int t;
memset(dp,-1,sizeof(dp));
cin>>t;
while(t--)
{
long long a,b;
cin>>a>>b;
if(a>b)
{
int tm=b;
b=a;
a=tm;
}
cout<<work(b)-work(a-1)<<'\n';
}
return 0;
}</span>
7.题目链接:hdu3652
【题意】数中出现13,[1,n]中有多少个这样的数
【思路】。。。
<span style="font-size:14px;">/*************************************************************************
> File Name: hdu3652.cpp
> Author: wanghao
> Mail: haohaoac@163.com
> Created Time: 2015年07月07日 星期二 00时18分32秒
************************************************************************/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int num[15];
int dp[12][13][2][2];
int dfs(int p,int yu,int flag1,int flag13,int flag)
{
if(p==-1)
return yu==0&&flag13;
if(!flag&&dp[p][yu][flag1][flag13]!=-1)//flag13也可单列出来,减小一维
return dp[p][yu][flag1][flag13];
int u=flag?num[p]:9;
int res=0;
for(int i=0;i<=u;i++)
{
res+=dfs(p-1,(yu*10+i)%13,i==1,flag13||(flag1&&i==3),flag&&i==num[p]);
}
// cout<<p<<' '<<yu<<' '<<flag1<<' '<<flag13<<' '<<flag<<endl;
// cout<<res<<endl;
if(!flag)
dp[p][yu][flag1][flag13]=res;
return res;
}
int work(int x)
{
int i=0;
while(x)
{
num[i++]=x%10;
x/=10;
}
return dfs(i-1,0,0,0,1);
}
int main()
{
memset(dp,-1,sizeof(dp));
int n;
while(scanf("%d",&n)!=EOF)
{
printf("%d\n",work(n));
}
return 0;
}</span>
8.题目链接:hdu4734
【题意】函数 F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A 1 * 1求[0,b]中f(i)比f(A)小的个数。
【思路】f最大为9*2^9.....+9*2^0=9*(2^10-1)<10*2^10,dp[len][sum]表示剩下长度为len,f值比sum小或相等的个数。奇怪的题目。。。
<span style="font-size:14px;">/*************************************************************************
> File Name: hdu4734.cpp
> Author: wanghao
> Mail: haohaoac@163.com
> Created Time: 2015年07月07日 星期二 00时18分32秒
************************************************************************/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int num[12];
int dp[12][5120];
int fa;
int f(int x)
{
int res=0;
int v=1;
while(x)
{
res=res+x%10*v;
x/=10;
v*=2;
}
return res;
}
int A[10];
int dfs(int p,int sum,int flag)
{
// cout<<p<<' '<<sum<<' '<<flag<<endl;
if(p==-1)
return sum>=0;
if(sum<0)
return 0;
if(!flag&&dp[p][sum]!=-1)
return dp[p][sum];
int u=flag?num[p]:9;
int res=0;
for(int i=0;i<=u;i++)
{
res+=dfs(p-1,sum-i*A[p],flag&&i==num[p]);
}
// cout<<p<<' '<<sum<<' '<<flag<<endl;
//cout<<res<<endl;
if(!flag)
dp[p][sum]=res;
return res;
}
int work(int x)
{
if(x==-1)
return 0;
int i=0;
while(x)
{
num[i++]=x%10;
x/=10;
}
// memset(dp,-1,sizeof(dp));
return dfs(i-1,fa,1);
}
int main()
{
A[0]=1;
for(int i=1;i<=9;i++)
A[i]=A[i-1]*2;
int t;
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
int T=t;
while(t--)
{
int a,b;
scanf("%d%d",&a,&b);
fa=f(a);
// cout<<"fa: "<<fa<<endl;
printf("Case #%d: %d\n",T-t,work(b));
}
return 0;
}</span>
9.题目链接:hdu4507
【思路】主要还是推平方和的公式 ∑(x+ai)^2=n*x^2+2*x*∑ai+∑ai^2,需要得到子问题的和及平方和及个数。细节地方一定要注意,个数为0与和为0是不一样的,和为0还有可能存在符合条件的数。求个数的时候一定要注意不要取模。。然而取模居然也能AC。。
<span style="font-size:14px;">/*************************************************************************
> File Name: hdu4507.cpp
> Author: wanghao
> Mail: haohaoac@163.com
> Created Time: 2015年07月07日 星期二 20时07分53秒
************************************************************************/
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
int mod=1000000007;
ll dp[20][7][7][3];
ll A[20];
int num[20];
void dfs(int p,int sum7,int value7,int flag,ll *sum,ll *sum2,ll *sumn)
{
if(p==-1)
{
*sum=*sum2=0;
if(sum7==0||value7==0)
*sumn=0;
else *sumn=1;
return ;
}
if(!flag&&dp[p][sum7][value7][2]!=-1)
{
*sum=dp[p][sum7][value7][0];
*sum2=dp[p][sum7][value7][1];
*sumn=dp[p][sum7][value7][2];
return ;
}
int u=flag?num[p]:9;
*sumn=*sum=*sum2=0;
int ok=0;
for(int i=0;i<=u;i++)
{
if(i==7)continue;
ll a,b,c;
dfs(p-1,(sum7+i)%7,(value7*10+i)%7,flag&&i==num[p],&a,&b,&c);
if(c==0)//不判也能ac...但不知道为毛
continue;
ok=1;
*sum2=(*sum2+c%mod*i*A[p]%mod*i*A[p]%mod+b+2*i*A[p]%mod*a%mod)%mod;
*sum=(*sum +c%mod*i*A[p]+a)%mod;
*sumn=*sumn+c;//别取模。。但取模也能a
}
// cout<<"df "<<p<<' '<<sum7<<' '<<value7<<' '<<flag<<endl;
// cout<<*sum<<' '<<*sum2<<' '<<*sumn<<endl;
if(!flag)
{
dp[p][sum7][value7][0]=*sum;
dp[p][sum7][value7][1]=*sum2;
dp[p][sum7][value7][2]=*sumn;
}
}
ll work(ll x)
{
// cout<<"work "<<x<<endl;
int i=0;
while(x)
{
num[i++]=x%10;
x/=10;
}
ll a,b,c;
dfs(i-1,0,0,1,&a,&b,&c);
// cout<<b<<endl;
return b;
}
int main()
{
int t;
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
for(int i=0;i<20;i++)
A[i]=i==0?1:(A[i-1]*10)%mod;
while(t--)
{
ll a,b;
cin>>a>>b;
cout<<(work(b)-work(a-1)+mod)%mod<<'\n';
}
return 0;
}</span>
PS: 就是模板题( ⊙ o ⊙ )啊!