HDU-4507-恨7不成妻(区间内满足条件的所有数的平方和)
//考虑每个数对平方和的贡献 比如123^2=(100+23)*(100+23);(a+b)^2=a^2+b^2+2*a*b;
//所以 foru(i,0,up)
//ans.cnt+=cur.cnt;
//ans.sum+=cur.sum+i*p[pos]*cur.cnt;
//ans.sqsum+=i*p[pos]*i*p[pos]*cur.cnt+cur.sqsum+2*i*p[pos]*cur.sum;
#include<cstdio>
#include<iostream>
#include<cstring>
#define m(a,b) memset(a,b,sizeof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=20+2,mod=1e9+7;
int a[N];ll p[N];
struct node{
ll cnt,sum,sqsum;
node(){cnt=-1,sum=sqsum=0;}
}dp[N][7][7];
node num_dp(int pos,int k1,int k2,int limit)
{
if(!pos){
node tmp;tmp.cnt=(k1&&k2);
return tmp;
}
if(!limit&&dp[pos][k1][k2].cnt!=-1) return dp[pos][k1][k2];
int up=limit?a[pos]:9;
node ans;ans.cnt=0;
for(int i=0;i<=up;i++)
{
if(i==7) continue;
node cur=num_dp(pos-1,(k1*10+i)%7,(k2+i)%7,limit&&i==a[pos]);
ans.cnt+=cur.cnt;ans.cnt%=mod;
ans.sum+=(cur.sum+p[pos]*i%mod*cur.cnt%mod)%mod;ans.sum%=mod;
ans.sqsum+=p[pos]*i%mod*p[pos]%mod*i%mod*cur.cnt%mod;
ans.sqsum+=(cur.sqsum+2*p[pos]%mod*i%mod*cur.sum%mod)%mod;ans.sqsum%=mod;
}
if(!limit) dp[pos][k1][k2]=ans;
return ans;
}
void init()
{
p[1]=1;
for(int i=2;i<N-2;i++)
p[i]=(p[i-1]*10)%mod;
}
int main()
{
init();ll x,y,tmp,ans1,ans2;
int T;scanf("%d",&T);
while(T--)
{
scanf("%lld%lld",&x,&y);--x;
if(x>y) tmp=x,x=y,y=tmp;
int p=0;
while(y) a[++p]=y%10,y/=10;
node ti=num_dp(p,0,0,1);ans1=ti.sqsum;
if(!x) ans2=0;
else{
p=0;
while(x) a[++p]=x%10,x/=10;
ti=num_dp(p,0,0,1),ans2=ti.sqsum;
}
printf("%lld\n",(((ans1-ans2)%mod+mod)%mod));//T__T
}
}
HDU-5179-beautiful number(前一位是后一位倍数且不为0)
//该题涉及到前导0,当高位前几位都是0000时,第pos位则不需要pre%i==0(因为此时pre==0)
//虽说1<=a[i]<=9,但foru(i,0,up) 要从0开始而不是1
//因为前几个高位可以为000,比1234 最后的数字难道不可以0093吗。
//但是这样会多枚举000000这个情况 最后应--ans 但是题目是要求区间内的ans2-ans1抵消了
//或者判断时改为if(lead&&(pos!=1||i)||i&&(pre%i==0))
//但是[x,y],--x之后x==0时num_dp直接返回1(代表数字0也是1种合理情况) 我们要杜绝他发生
//所以if(p) ans1=num_dp()
#include<cstdio>
#include<iostream>
#include<cstring>
#define m(a,b) memset(a,b,sizeof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=10+2;
int a[N];ll dp[N][N];
ll num_dp(int pos,int pre,int lead,int limit)
{
if(!pos) return 1;
if(!limit&&!lead&&dp[pos][pre]!=-1) return dp[pos][pre];
int up=limit?a[pos]:9;ll ans=0;
for(int i=0;i<=up;i++)
if(lead&&(pos!=1||i)||i&&(pre%i==0))
ans+=num_dp(pos-1,i,lead&&(i==0),limit&&i==a[pos]);
if(!limit&&!lead) dp[pos][pre]=ans;
return ans;
}
int main()
{
m(dp,-1);
int T;scanf("%d",&T);
while(T--)
{
ll x,y,ans1=0,ans2=0;scanf("%lld%lld",&x,&y),--x;
int p=0;
while(x) a[++p]=x%10,x/=10;
if(p) //(不可以写x!!!我是傻逼啊啊 经过上述while(x),对x拆分已经使x变为了0)
ans1=num_dp(p,-1,1,1);//防止x开始==0时,num_dp直接返回1
p=0;
while(y) a[++p]=y%10,y/=10;
ans2=num_dp(p,-1,1,1);
printf("%lld\n",(ans2-ans1));
}
}
HDU-4734-F(x)(区间内f(x)<=f[k]的个数)
思路:dp[pos][nexsum]代表剩下的pos个低位对f[x]的值的贡献应<=nexsum的这样的数的个数。
//由于多组数据要重复利用
//dp[pos][nexsum]表示剩下的pos位对该数字大小的贡献应<=nexsum;
//如此边不需要memset
#include<cstdio>
#include<iostream>
#include<cstring>
#define m(a,b) memset(a,b,sizeof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=+92,Max=4599;
int a[N];
ll dp[N][Max+5],p[N];
ll num_dp(int pos,int pre,int nexsum,int limit)
{
if(nexsum<0) return 0;
if(!pos) return 1;
if(!limit&&dp[pos][nexsum]!=-1) return dp[pos][nexsum];
int up=limit?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++) ans+=num_dp(pos-1,i,nexsum-i*p[pos],limit&&i==a[pos]);
if(!limit) dp[pos][nexsum]=ans;
return ans;
}
int main()
{
m(dp,-1);
int T,cnt=0;scanf("%d",&T); p[1]=1;
for(int i=2;i<=9;i++) p[i]=p[i-1]*2;
while(T--)
{
ll x,y;scanf("%lld%lld",&x,&y);
ll KK=0;int k=1,p=0;
while(x) KK+=x%10*k,x/=10,k*=2;
while(y) a[++p]=y%10,y/=10;
printf("Case #%d: %lld\n",++cnt,(num_dp(p,-1,KK,1)));
}
}
HDU-3652-B-number(区间内数平衡数的个数)
题意:给出区间 [l,r] 内平衡数的个数,平衡数是指,以某位作为支点,此位左边的 数字乘以距离 的和与右边的相等.
思路:枚举支点位置 。
dp[pos][dex][sum],既然这是杠杆,就直接sum+=i*(pos-mid)
(i是该位选取的数,pos-mid是力臂有正有负)。if(!pos) return !sum;
#include<cstdio>
#include<iostream>
#include<cstring>
#define m(a,b) memset(a,b,sizeof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=20,Max=17*18*9/2;
int a[N],mid;
ll dp[N][N][Max+5];
ll num_dp(int pos,int sum,int limit)
{
if(sum<0) return 0;//剪枝(必须)
if(!pos) return (!sum);
if(!limit&&dp[pos][mid][sum]!=-1) return dp[pos][mid][sum];
int up=limit?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++) ans+=num_dp(pos-1,sum+i*(pos-mid),limit&&i==a[pos]);
if(!limit) dp[pos][mid][sum]=ans;
return ans;
}
int main()
{
m(dp,-1);
int T;scanf("%d",&T);
while(T--)
{
ll x,y,ans1=0,ans2=0;scanf("%lld%lld",&x,&y),--x;
int p=0;
while(x) a[++p]=x%10,x/=10;
for(int i=1;i<=p;i++) mid=i,ans1+=num_dp(p,0,1);
ans1-=p-1,p=0;//每一次枚举不同的支点 00000000(p位)都满足题意的 计算了p个0 实际上只需要1个 所以ans-=p-1;
while(y) a[++p]=y%10,y/=10;
for(int i=1;i<=p;i++) mid=i,ans2+=num_dp(p,0,1);
ans2-=p-1;
printf("%lld\n",(ans2-ans1));
}
}
HDU-3555-Bomb(0-n中数字序列存在49的个数)
#include<cstdio>
#include<iostream>
#include<cstring>
#define m(a,b) memset(a,b,sizeof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=900+5,M=1e6+5;
int a[N];ll dp[N][3];
ll num_dp(int pos,int sta,int limit)//sta=2:[pos+1,tot]存在49.
{ //sta=1:[pos+1,tot]不存在49&&a[pos+1]==4.
if(!pos) return sta==2; //sta=0:[pos+1,tot]不存在49&&a[pos+1]!=4.
if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
int up=limit?a[pos]:9,nsta;
ll ans=0;
for(int i=0;i<=up;i++)
{
if(sta==2) nsta=2;
if(!sta) nsta=(i==4)?1:0;
if(sta==1){
if(i==9) nsta=2;
else if(i==4) nsta=1;
else nsta=0;
}
ans+=num_dp(pos-1,nsta,limit&&i==a[pos]);
}
if(!limit) dp[pos][sta]=ans;
return ans;
}
char s[100];
int main()
{
int T;scanf("%d",&T);
m(dp,-1);
while(T--)
{
scanf("%s",s);int pos=0,l=strlen(s);
for(int i=l-1;i>=0;i--)//注意:这里将顺序颠倒了过来 num_dp的时候还相当于从高位向低位深搜(枚举)
a[++pos]=s[i]-'0';//装的是个十百千万这种顺序
printf("%lld\n",(num_dp(pos,0,1)));
}
}
HDU-3652-B-number(找出1~n范围内含有13并且能被13整除的数字的个数)
dp[pos][sta][mod] ,mod记录已经搜过的那几个高位%13的余数
nmod=(mod*10+i)%13;
#include<cstdio>
#include<iostream>
#include<cstring>
#define m(a,b) memset(a,b,sizeof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=10+2,M=1e6+5;
int a[N];
ll dp[N][3][13];
ll num_dp(int pos,int sta,int k,int limit)
{
if(!pos) return sta==2&&!k;
if(!limit&&dp[pos][sta][k]!=-1) return dp[pos][sta][k];
int up=limit?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++)
{
int nsta,tmp;
if(sta==2) nsta=2;
if(!sta) nsta=(i==1)?1:0;
if(sta==1)
{
if(i==3) nsta=2;
else if(i==1) nsta=1;
else nsta=0;
}
tmp=(k*10+i)%13;
ans+=num_dp(pos-1,nsta,tmp,limit&&i==a[pos]);
}
if(!limit) dp[pos][sta][k]=ans;
return ans;
}
int main()
{
m(dp,-1);int n;
while(~scanf("%d",&n))
{
int p=0;
while(n) a[++p]=n%10,n/=10;
printf("%lld\n",(num_dp(p,0,0,1)));
}
}