【HDU4507】Seven-數位DP

測試地址:Seven
作法: 本題須要用到數位DP。
是的,我看這題題目名字實在太長,就本身給取了個名字…首先看它給的條件,若是隻求數字個數的話,很是明顯是數位DP的形式,只須要設 n u m ( i , j , k ) num(i,j,k) 爲前 i i 位中,不卡上界的,數位和對 7 7 的餘數爲 j j ,數對 7 7 的餘數爲 k k 的數字的個數,很容易就能轉移。而要求數字的和較難一點,但咱們也能較快地想出轉移。而如今要求數字的平方和,這就有點複雜了。
然而實際上也不很複雜,你只須要一個結論:一些數的平方和,等於這些數和的平方,減去這些數兩兩間的乘積之和乘 2 2 。這個結論也很容易理解,由於 ( a i ) 2 = ( a i a j ) = a i 2 + i j a i a j (\sum a_i)^2=\sum (a_i\sum a_j)=\sum a_i^2+\sum_{i\ne j} a_i\cdot a_j ,移項便可獲得上面的結論。這樣一來,咱們須要求 f ( i , j , k ) f(i,j,k) ,表示知足對應條件(和上面狀態定義同樣,就不重複寫了)的數的和,還有 g ( i , j , k ) g(i,j,k) ,表示知足對應條件的數兩兩之間乘積的和。
咱們發現全部轉移均可以轉化爲如下問題:咱們有兩個集合,分別求出了對應的 f , g f,g n u m num ,它們合併以後的 f , g f,g n u m num 怎麼求?這個比較好推,就本身推一下就好了,實在不懂就看我代碼。還有一個問題是,咱們有一個集合,求出了對應的 f , g f,g n u m num ,那麼在這些集合的數以後添加一位數 x x 後,集合的 f , g f,g n u m num 怎麼變化?這個就稍微複雜一些,以三個數的集合 S = { a , b , c } S=\{a,b,c\} 爲例,令新加的位爲 x x ,則:
( 10 a + x ) + ( 10 b + x ) + ( 10 c + x ) = 10 ( a + b + c ) + 3 x (10a+x)+(10b+x)+(10c+x)=10(a+b+c)+3x
( 10 a + x ) ( 10 b + x ) + ( 10 b + x ) ( 10 c + x ) + ( 10 a + x ) ( 10 c + x ) = 100 ( a b + b c + a c ) + 20 ( a + b + c ) x + 3 x 2 (10a+x)(10b+x)+(10b+x)(10c+x)+(10a+x)(10c+x)=100(ab+bc+ac)+20(a+b+c)x+3x^2
根據這兩個式子咱們能夠找到規律,首先 n u m num 不變, f f 就等於原來的 f f 10 10 加上 x x n u m num ,而 g g 等於原來的 g g 100 100 ,加上 10 ( n u m 1 ) 10(num-1)\cdot 原來的 f f ,最後加上 n u m ( n u m 1 ) 2 x 2 \frac{num(num-1)}{2}\cdot x^2
有了這些東西,就能夠解決轉移的問題了,像模板數位DP同樣作就好了。
如下是本人代碼:php

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
const ll inv2=500000004;
int T,n;
ll s[25],num[25][7][7],f[25][7][7],g[25][7][7];

void update(int dgt,int j,int k,ll nowf,ll nowg,ll nownum)
{
	g[dgt][j][k]=(g[dgt][j][k]+nowg+nowf*f[dgt][j][k])%mod;
	f[dgt][j][k]=(f[dgt][j][k]+nowf)%mod;
	num[dgt][j][k]=(num[dgt][j][k]+nownum)%mod;
}

ll solve()
{
	int nowi=0,nowj=0,nowk=0;
	ll nowsum=0;
	memset(f[n+1],0,sizeof(f[n+1]));
	memset(g[n+1],0,sizeof(g[n+1]));
	memset(num[n+1],0,sizeof(num[n+1]));
	for(int dgt=n;dgt>=1;dgt--)
	{
		memset(f[dgt],0,sizeof(f[dgt]));
		memset(g[dgt],0,sizeof(g[dgt]));
		memset(num[dgt],0,sizeof(num[dgt]));
		for(int j=0;j<7;j++)
			for(int k=0;k<7;k++)
				for(int now=0;now<=9;now++)
				{
					if (now==7) continue;
					int newj=(j+now)%7,newk=(k*10ll+now)%7;
					ll nowf=(f[dgt+1][j][k]*10ll%mod+now*num[dgt+1][j][k]%mod)%mod;
					ll nowg=g[dgt+1][j][k]*100ll%mod;
					nowg=(nowg+f[dgt+1][j][k]*now%mod*10ll%mod*(num[dgt+1][j][k]-1ll+mod)%mod)%mod;
					nowg=(nowg+num[dgt+1][j][k]*(num[dgt+1][j][k]-1ll)%mod*inv2%mod*now*now%mod)%mod;
					ll nownum=num[dgt+1][j][k];
					update(dgt,newj,newk,nowf,nowg,nownum);
				}
		
		if (dgt<n&&(!nowi))
		{
			for(int now=0;now<s[dgt];now++)
			{
				if (now==7) continue;
				int newj=(nowj+now)%7,newk=(nowk*10ll+now)%7;
				update(dgt,newj,newk,(nowsum*10ll+now)%mod,0ll,1ll);
			}
		}
		nowi=(nowi||(s[dgt]==7));
		nowj=(nowj+s[dgt])%7;
		nowk=(nowk*10ll+s[dgt])%7;
		nowsum=(nowsum*10ll+s[dgt])%mod;
		
		for(int now=1;now<=((dgt==n)?(s[dgt]-1):9);now++)
		{
			if (now==7) continue;
			update(dgt,now%7,now%7,now,0ll,1ll);
		}
	}
	
	f[0][0][0]=g[0][0][0]=num[0][0][0]=0;
	for(int j=1;j<7;j++)
		for(int k=1;k<7;k++)
			update(0,0,0,f[1][j][k],g[1][j][k],num[1][j][k]);
	ll ans=((f[0][0][0]*f[0][0][0]%mod-g[0][0][0]*2ll)%mod+mod)%mod;
	if ((!nowi)&&nowj&&nowk) ans=(ans+nowsum*nowsum)%mod;
	return ans;
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		ll ans=0,x;
		
		scanf("%lld",&x);
		x--;
		if (x)
		{
			n=0;
			while(x)
			{
				s[++n]=x%10;
				x/=10;
			}
			ans=(mod-solve())%mod;
		}
		
		scanf("%lld",&x);
		n=0;
		while(x)
		{
			s[++n]=x%10;
			x/=10;
		}
		ans=(ans+solve())%mod;
		printf("%lld\n",ans);
	}
	
	return 0;
}
相關文章
相關標籤/搜索