数位dp
dp[pos][flag][limit] 代表从高到低到了第pos位置,从第pos位到第0位之间在状态位flag的情况下是否有limit条件下的答案是多少,以不要62这道题目为例子在这里比方说都是到了第万位数,需要确定的只有前面是否是6,其他的答案是一样多的,并且需要注意的是只有没有limit限制的时候才可以进行记忆化搜索,因为如果有limit限制的话,这个情况只会出现一次,存下来是没有用的
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+5;
int dp[maxn][2];
int digit[maxn];
int len;
void cal(int n){
memset(dp,-1,sizeof(dp));
len=0;
while(n){
digit[++len]=n%10;
n/=10;
}
}
int dfs(int pos,int flag,int limit){
int ret=0;
if(!pos){
return 1;
}
if(!limit&&dp[pos][flag]!=-1){
return dp[pos][flag];
}
int up=limit?digit[pos]:9;
for(int i=0;i<=up;i++){
if(i==4&&flag||i==2){
continue;
}
ret+=dfs(len-1,i==6,limit&&i==up);
}
if(!limit){
dp[pos][flag]=ret;
}
return ret;
}
signed main(){
int x,y;
while(cin>>x>>y){
cal(y);
int you=dfs(len,0,1);
cal(x-1);
int zuo=dfs(len,0,1);
cout<<you-zuo<<"\n";
}
}
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e2+5;
int digit[maxn],dp[maxn][2][2];
int len,need;
int ans1[maxn],ans2[maxn],w[maxn];
void cal(int x){
len=0;
int sta=1;
while(x){
digit[++len]=x%10;
x/=10;
w[len]=w[len-1]+digit[len]*sta;
sta*=10;
}
if(len==0){
digit[++len]=0;
}
}
int dfs(int pos,int check,int limit){
if(pos==0){
return 0;
}
if(!limit&&dp[pos][check][limit]!=-1){
return dp[pos][check][limit];
}
int up=limit?digit[pos]:9;
int res=0;
for(int i=0;i<=up;i++){
res+=dfs(pos-1,check&&i==0,limit&&i==up);
if(i==need){
if(check&&need==0){
if(pos==1){
res++;
}
}
else{
if(limit&&i==up){
res+=w[pos-1]+1;
}
else{
res+=pow(10,pos-1);
}
}
}
}
dp[pos][check][limit]=res;
return res;
}
signed main(){
int x,y;
while(cin>>x>>y){
if(x==0&&y==0){
return 0;
}
if(x>y){
swap(x,y);
}
cal(y);
for(int i=0;i<=9;i++){
for(int j=0;j<=100;j++){
for(int check=0;check<=1;check++){
for(int limit=0;limit<=1;limit++){
dp[j][check][limit]=-1;
}
}
}
need=i;
ans2[i]=dfs(len,1,1);
}
cal(x-1);
for(int i=0;i<=9;i++){
for(int j=0;j<=100;j++){
for(int check=0;check<=1;check++){
for(int limit=0;limit<=1;limit++){
dp[j][check][limit]=-1;
}
}
}
need=i;
ans1[i]=dfs(len,1,1);
cout<<ans2[i]-ans1[i]<<' ';
}
cout<<"\n";
}
}
通过分析可以知道其实就是i从0-x,j从0-y,并且i和j不能同时为0时式子的和是多少,然后对于i和j,它们相与为0,对答案的贡献是他们最高位的1为位数是第几位,比如i等于1001,j等于0100,那么最高位是i的第四个1,这个i和j对于答案的贡献就是4,可以设计dp方程dp[pos][x][y]代表到了第pos位i和j没有相同时,i选择为x,j选择为y有多少种组合
#include<bits/stdc++.h>
#define int long long
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int maxn=2e5+5;
const int inf=1e9+7;
const int mod=1e9+7;
int x[32],y[32],cnt1,cnt2;
int dp[32][2][2]; //代表最高位是第pos位,目前f1=i,f2=j有多少种选择
int a,b;
int dfs(int pos,int f1,int f2){
if(!pos){
return 1;
}
if(dp[pos][f1][f2]!=-1){
return dp[pos][f1][f2];
}
int upa=f1?x[pos]:1;
int upb=f2?y[pos]:1;
int res=0;
for(int i=0;i<=upa;i++){
for(int j=0;j<=upb;j++){
if((i&j)==0){
res=(res+dfs(pos-1,f1&&i==upa,f2&&j==upb))%mod;
}
}
}
return dp[pos][f1][f2]=res;
}
int init(){
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
memset(dp,-1,sizeof(dp));
cnt1=0,cnt2=0;
while(a){
x[++cnt1]=a&1;
a>>=1;
}
while(b){
y[++cnt2]=b&1;
b>>=1;
}
int res=0;
for(int i=1;i<=max(cnt1,cnt2);i++){
if(i<=cnt1) res=(res+dfs(i-1,i==cnt1,i>cnt2)*i%mod)%mod; //代表最高位为1的是a
if(i<=cnt2) res=(res+dfs(i-1,i>cnt1,i==cnt2)*i%mod)%mod; //代表最高位为1的是b
}
return res;
}
void solve(){
cin>>a>>b;
cout<<init()<<"\n";
}
signed main(){
int t=1;
cin>>t;
while(t--){
solve();
}
}
题目描述:
给定L,R,问[L,R]的与7无关的数的平方的和是多少
思路:
在有限的空间内,用dp可以记录下来的前面的状态有各个位上的数的和的大小(%7)shuhe,以及前面的数的大小(%7)sumhe,用这两个条件显然不能求出答案,考虑把一个数分为2部分,123abc,那么假设现在到了a这个位置,我们前面是123,后面随意组合,则dp[pos][shuhe][sumhe]可以记录一下他到最低位的有效的数的平方和sum2,他到最低位的有效的数的和sum1,以及他到最低为的有效的数的个数cnt,对sum2的贡献即为(123*10^3)^2+(2*123*10^3*abc)+(abc)^2,细节再考虑一下怎么记录下来就好
#include<bits/stdc++.h>
#define int long long
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int maxn=30;
const int inf=1e9+7;
const int mod=1e9+7;
int p[maxn];
int cnt=0;
int digit[maxn];
struct node{
int cnt,sum1,sum2;
//sum1是数的和,sum2是数的平方和
};
void cal(int x){
cnt=0;
while(x){
digit[++cnt]=x%10;
x/=10;
}
}
node dp[maxn][10][10];
node dfs(int pos,int weihe,int sumhe,int flag){
if(pos==0){
return {weihe&&sumhe,0,0};
}
if(!flag&&dp[pos][weihe][sumhe].cnt!=-1){
return dp[pos][weihe][sumhe];
}
int upper=flag?digit[pos]:9;
node res={0,0,0};
for(int i=0;i<=upper;i++){
if(i==7){
continue;
}
else{
node tmp=dfs(pos-1,(weihe+i)%7,(sumhe*10+i)%7,flag&&i==upper);
res.cnt+=tmp.cnt;
res.cnt%=mod;
int now=i*p[pos-1]%mod;
res.sum1+=tmp.sum1+now*tmp.cnt;
res.sum1%=mod;
res.sum2+=now*i%mod*p[pos-1]%mod*tmp.cnt%mod+2*now*tmp.sum1%mod+tmp.sum2%mod;
res.sum2%=mod;
}
}
if(!flag){
dp[pos][weihe][sumhe]=res;
}
return res;
}
void init(){
p[0]=1;
for(int i=1;i<=20;i++){
p[i]=p[i-1]*10%mod;
}
}
void solve(){
int l,r;
cin>>l>>r;
cal(r);
for(int i=0;i<=29;i++){
for(int j=0;j<=9;j++){
for(int k=0;k<=9;k++){
dp[i][j][k].cnt=-1;
dp[i][j][k].sum1=0;
dp[i][j][k].sum2=0;
}
}
}
int ans2=dfs(cnt,0,0,1).sum2;
cal(l-1);
for(int i=0;i<=29;i++){
for(int j=0;j<=9;j++){
for(int k=0;k<=9;k++){
dp[i][j][k].cnt=-1;
dp[i][j][k].sum1=0;
dp[i][j][k].sum2=0;
}
}
}
int ans1=dfs(cnt,0,0,1).sum2;
cout<<(ans2+mod-ans1)%mod<<"\n";
}
signed main(){
init();
int t=1;
cin>>t;
while(t--){
solve();
}
}
思路:
数位dp记录dp[cur][s][lv] cur:当前位数 s:当前有多少个数顶着上界 lv:还需要加多少值才能等于k
对于每一位枚举其1的个数和0的个数,乘以这样分配有多少种方法,即可算出这一位的贡献然后加一定的剪枝即可过题
#include<bits/stdc++.h>
#define int long long
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int maxn=105;
const int inf=1e9+7;
const int mod=1e9+7;
map<int,int>dp[maxn][maxn];
int c[maxn][maxn];
int digit[maxn];
int cnt;
void cal(int x){
while(x){
digit[++cnt]=x%2;
x/=2;
}
}
int n,m,k;
int dfs(int cur,int s,int lv){ //s:当前有多少个数顶着上界 lv:还需要加多少值才能等于k
if(lv<0){
return 0;
}
if(cur==0){
return (lv==0);
}
int s1=k/2,s0=k-s1;
if(((1ll<<cur)-1)*s0*s1<lv){
return 0;
}
if(dp[cur][s].count(lv)){
return dp[cur][s][lv];
}
int res=0;
if(digit[cur]==1){
//当前位上界是1
for(int i=0;i<=s;i++){
//选择i个赋值为1
for(int j=0;j<=k-s;j++){
//选择j个赋值为1
int val=1ll*(k-i-j)*(i+j)*(1ll<<(cur-1));
int t=c[s][i]*c[k-s][j]%mod;
res+=t*dfs(cur-1,i,lv-val)%mod;
res%=mod;
}
}
}
else{
for(int i=0;i<=0;i++){ //顶着上界的数一个都不能选
for(int j=0;j<=k-s;j++){ //枚举现在选择多少1
int val=1ll*(k-i-j)*(i+j)*(1ll<<(cur-1));
int t=c[s][i]*c[k-s][j]%mod;
res+=t*dfs(cur-1,s,lv-val)%mod;
res%=mod;
}
}
}
return dp[cur][s][lv]=res;
}
void solve(){
for(int i=0;i<=100;i++){
for(int j=0;j<=i;j++){
if(i==0||j==0){
c[i][j]=1;
}
else{
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
}
cin>>n>>m>>k;
cal(m);
cout<<dfs(cnt,k,n)<<"\n";
}
signed main(){
int t=1;
//cin>>t;
while(t--){
solve();
}
}