【CF55D】Beautiful Numbers-數位DP+優化

測試地址:Beautiful Numbers
題目大意: 求在區間 [ L , R ] [L,R] 中,有多少能整除自身全部非零數位的數。
作法: 本題須要用到數位DP+優化。
首先這題一看就是數位DP,本題的關鍵是狀態的設計以及優化。
咱們很快能寫出一個狀態定義: f ( i , j , k , 0 / 1 ) f(i,j,k,0/1) 表示前 i i 位,全部非零位的LCM是 j j ,對 j j 的餘數是 k k ,不卡/卡上界的數的數目。但咱們發現這個東西不能轉移,當 j j 增長的時候,新的餘數會有不少種狀況,所以咱們不能考慮這種狀態定義。
咱們發現,涉及到的全部的 j j 都是 L C M ( 1 , 2 , . . . , 9 ) = 2520 LCM(1,2,...,9)=2520 的因數,因此咱們只須要 k k 的表示改爲對 2520 2520 的餘數,那麼 k k j j 的整數倍時,就表示這些數知足題目中的條件。
這樣咱們就能轉移了。但分析一下發現,時間複雜度是 19 19 (位數) × 2520 × 2520 × log 2520 \times 2520\times 2520\times \log 2520 (求LCM) × 2 × 10 \times 2\times 10 (枚舉轉移的位) × 10 \times 10 (數據組數),T到沒邊,所以咱們還要進一步進行優化。
打表或手算髮現,不一樣的 j j 的數目,也就是 2520 2520 的因數,只有 48 48 個,所以用一個數組映射一下這些數, j j 用映射後的值表示便可,優化掉一個 50 50 。進而咱們發現咱們能夠預處理出這些數之間的LCM,因此又優化掉了一個 log 2520 \log 2520 。進行這兩個優化後,已經很是接近時限了,但仍是會T掉。進一步觀察發現,卡上界的狀況中只有一個數,這個數的 j , k j,k 是肯定的,不用跟隨上面的枚舉,所以咱們根本不用存 0 / 1 0/1 一維,只要維護當前上界的 j , k j,k 便可完成應完成的轉移。因此又優化掉了一個 2 2 ,就能夠經過此題了,最大點的時間爲 3 s 3s 左右。
如下是本人代碼:html

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int LCM=2520;
int T,n,s[20],lcmlist[1050],id[3010],L[50][10],tot;
ll f[21][LCM+10][50];

int gcd(int a,int b)
{
	return (b==0)?a:gcd(b,a%b);
}

int lcm(int a,int b)
{
	return a*b/gcd(a,b);
}

ll solve()
{
	memset(f[n+1],0,sizeof(f[n+1]));
	int toplcm=1,toprem=0;
	
	for(int i=n;i>=1;i--)
	{
		memset(f[i],0,sizeof(f[i]));
		for(int j=0;j<LCM;j++)
			for(int k=1;k<=tot;k++)
				for(int now=0;now<=9;now++)
					f[i][(j*10+now)%LCM][L[k][now]]+=f[i+1][j][k];
		if (i<n)
		{
			for(int now=0;now<s[i];now++)
				f[i][(toprem*10+now)%LCM][L[toplcm][now]]++;
		}
		for(int j=1;j<=((i==n)?(s[i]-1):9);j++)
			f[i][j][id[j]]++;
		toplcm=L[toplcm][s[i]];
		toprem=(toprem*10+s[i])%LCM;
	}
	ll ans=0;
	for(int i=1;i<=tot;i++)
		for(int j=0;j*lcmlist[i]<LCM;j++)
			ans+=f[1][j*lcmlist[i]][i];
	if (toprem%lcmlist[toplcm]==0) ans++;
	return ans;
}

int main()
{
	scanf("%d",&T);
	
	tot=0;
	for(int i=1;i<(1<<9);i++)
	{
		int x=1;
		for(int j=1;j<=9;j++)
			if ((1<<(j-1))&i) x=lcm(x,j);
		lcmlist[++tot]=x;
	}
	sort(lcmlist+1,lcmlist+tot+1);
	tot=0;
	for(int i=1;i<(1<<9);i++)
		if (i==1||lcmlist[i]!=lcmlist[tot])
		{
			lcmlist[++tot]=lcmlist[i];
			id[lcmlist[tot]]=tot;
		}
	for(int i=1;i<=tot;i++)
		for(int j=0;j<=9;j++)
			L[i][j]=id[lcm(lcmlist[i],max(j,1))];
	
	while(T--)
	{
		ll x;
		scanf("%I64d",&x);
		x--;
		
		ll ans=0;
		if (x)
		{
			n=0;
			while(x)
			{
				s[++n]=x%10;
				x/=10;
			}
			ans-=solve();
		}
		
		scanf("%I64d",&x);
		n=0;
		while(x)
		{
			s[++n]=x%10;
			x/=10;
		}
		ans+=solve();
		printf("%I64d\n",ans);
	}
	
	return 0;
}
相關文章
相關標籤/搜索