CF55D||noi.ac模拟赛第二场 数位dp

原题:http://codeforces.com/problemset/problem/55/D

题解:题意给一个区间[l,r],求区间中可以被所有非0数位整除的数的个数。显然可以把区间拆开,[1,l-1]和[1,r]。问题就转化成求1-n的个数。可以发现1-9的最大公约数为2520。考虑如何转化,如果一个数a可以被所有非0数位整除,那他一定可以被2520的一个因整除。这个性质很容易证明任意一个数的数位一定是1-9的子集,一定是2520的因子,不妨记下mod2520的余数和最小公倍数。若可整除就是答案。

考虑数位dp。设f[i][s][k][0/1]表示前i位被2520除余数是S的答案是多少。0/1表示是否到上界。

转移f[i][s][k]=f[i-1][(mode*10+j)%2520][lcm(j,k)]用记忆化搜索解决。

这样的复杂度为O(T * 位数*2520*2520*10*2)这样是过不了的。

因为k只有48个考虑离散化,做个映射就好了

细节:

1.f数组只用清一遍-1

2.从高位到低位枚举

到这里这题就可以过了。。

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int M=2520; 
vector<int> dim;
int a[M+10],t,cur;
ll l,r,f[20][M+1][50];
inline int gcd_(int x,int y){return x%y?gcd_(y,x%y):y; } 
inline int lcm_(int x,int y){if(!y) return x;else return x/gcd_(x,y)*y;}
ll dfs(int x,int mode,int lcm,int op){
    if(!x) return mode%lcm==0?1:0;
    if(!op && ~f[x][mode][a[lcm]]) return f[x][mode][a[lcm]];
    int maxx=op?dim[x]:9;ll ret=0;
    for(int i=0;i<=maxx;i++) ret+=dfs(x-1,(mode*10+i)%M,lcm_(lcm,i),op&(i==maxx));
    if(!op) f[x][mode][a[lcm]]=ret;
    return ret;
}
ll work(ll x){
    dim.clear();dim.push_back(-1);ll t=x;
    while(t) dim.push_back(t%10),t/=10;
    return dfs(dim.size()-1,0,1,1);	
}
int main(){
    for(int i=1;i<=M;i++) if(M%i==0) a[i]=++cur;
    scanf("%d",&t);
    memset(f,-1,sizeof f);
    while(t--){
        scanf("%lld%lld",&l,&r);
        printf("%lld\n",work(r)-work(l-1));
    } 
    return 0;
} 

出题人的题解中给了一种优化。若  x | a*10^b,那么x的后b位一定是0且前面的数可以整除a。所以只需要记mode%252和最后一位就行了。

优化版本:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
#define ll long long 
using namespace std;
const int M=2520; 
vector<int> dim;
int a[M+10],t,cur;
ll l,r,f[20][253][50];
inline int gcd_(int x,int y){return x%y?gcd_(y,x%y):y; } 
inline int lcm_(int x,int y){if(!y) return x;else return x/gcd_(x,y)*y;}
inline ll rd(){
	ll x=0;ll f=1;char s=getchar();
	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
	while(isdigit(s)) x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return x*f;
}
ll dfs(int x,int mode,int lcm,int op){
	if(!x) return mode%lcm==0?1:0;
	if(!op && ~f[x][mode][a[lcm]]) return f[x][mode][a[lcm]];
	ll maxx=op?dim[x]:9;ll ret=0;
	for(ll i = 0; i <= maxx; i++){
		// 注意 i=0时 大佬的玄学优化 
		ret += dfs(x-1, x > 1 ? (mode*10+i)%252 : mode*10+i, lcm_(lcm,i), op&(i==maxx));
	}
	if(!op) f[x][mode][a[lcm]]=ret;
	return ret;
}
ll work(ll x){
	dim.clear();dim.push_back(-1);ll t=x;
	while(t) dim.push_back(t%10),t/=10;
	return dfs(dim.size()-1,0,1,1);	
}
int main(){
//	freopen("cf55d.in","r",stdin);
	for(int i=1;i<=M;i++) if(M%i==0) a[i]=++cur;
	t=rd();
	memset(f,-1,sizeof f);//只用清一遍-1 
	while(t--){
		l=rd();r=rd();//将区间拆开 
		printf("%lld\n",work(r)-work(l-1));
	} 
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/weixin_39689721/article/details/88652875
今日推荐