數位dp

emmmm一些技巧???

先上題目!

windy數 https://www.luogu.org/problem/P2657

數位dp最基礎的入門了呢。

重要技巧 (劃重點嘻嘻嘻)

1.如果要求求l-r的區間立的話,轉化成[1,r]-[1,l-1],即前綴和的形式,因爲這樣會很方便qwq

2. dp的話要從最高位開始,原因是你要判斷是否超過了邊界(就是如果前i位都和邊界相同,那麼i+1位置就要)但作爲一個懶人,我們一般存數的時候,都是從最低爲開始存貯。所以爲了方便,我們記憶化搜索的時候也可以從高到低搞(最後到了0就弄出去!)

3.個人建議把所有的要用的東西(就是dfs的所有參數)都存下來,因爲這樣寫真的超級超級方便吶!(開心)

4.別的沒啥了嘛。。。哦哦哦一般要用的limit(判斷前i位是否已經到達飽和)還有注意前導0啊啊啊啊我死了

代碼來啦(我知道你只關注這個www)

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
int s[30];
ll f[30][15][2][2];
ll dfs(int pos,int pre,int limit,int rt)
{
//	cout<<pos<<endl;
	if(pos==0)
	{
//		cout<<"here"<<endl;
		return 1;	
	}
	if(f[pos][pre][limit][rt]!=-1) 
	{
		return f[pos][pre][limit][rt];	
	//	cout<<"here"<<endl;
	}
	int m=9;if(limit) m=s[pos];
	ll res=0;
	for(int i=0;i<=m;i++)
	{
		if(!rt&&abs(pre-i)<2) continue;
		res+=dfs(pos-1,i,(limit&&(i==m)),rt&&i==0);		
	}
//	cout<<res<<endl;
	f[pos][pre][limit][rt]=res;
	return res;
}
ll workt(ll x)
{
	memset(f,-1,sizeof(f));
	int cnt=0;while(x) {s[++cnt]=x%10;x/=10;}
//	cout<<cnt<<endl;
	return dfs(cnt,-2,1,1);
}
int main()
{
	ll A,B;scanf("%lld%lld",&A,&B);
	printf("%lld",workt(B)-workt(A-1));
//	cout<<workt(0)<<endl;
	return 0;
}

第二題(你覺得我會告訴你我只做了兩題嗎)

同類分佈 https://www.luogu.org/problem/P4127

1.比剛纔那題難一些(這是廢話)

2.驚人發現:判斷是否非0竟然比判斷是否是-1快!(好蠢啊www)

3.似乎沒啥了呢,思維難度高一些,代碼沒啥區別。。。(x是位置,y是limit,a是數字和,b是mod的餘數)

代碼(忽略我爲了卡常的醜陋www不要欺負我)

#include <iostream>
#include <cstring>
#define ll long long
using namespace std;
ll f[27][3][300][300];
int vis[27][3][300][300];
int s[27];
int mod;
void chai(ll x)
{
	int cnt=0;
	while(x>0)
	{
		cnt++;
		s[cnt]=x%10;
		x/=10;
	}
}
ll find(int x,int y,int a,int b)
{
	if(x==0&&a==mod&&b==0) return 1;
	if(x==0) return 0;
	if(a>mod) return 0;
	if(vis[x][y][a][b]) 
		return f[x][y][a][b];
	vis[x][y][a][b]=1;
	int maxv=9;
	if(y==1) maxv=s[x];
	ll ans=0;
	for(int i=0;i<=maxv;i++)
	{
		ans+=find(x-1,y==1&&i==s[x],a+i,(b*10+i)%mod);
	}
	f[x][y][a][b]=ans;
	return ans;
}
ll work(ll x,int y)
{
	memset(s,0,sizeof(s));
	memset(vis,0,sizeof(vis));
	chai(x);
//	for(int i=1;i<=3;i++) cout<<s[i]<<" ";
	mod=y;
	return find(19,1,0,0);
}
int main()
{
	ll a,b;
	cin>>a>>b;
	ll ans=0;
	if(b==1000000000000000000ll) ans =29410615796612778 ;
	for(int i=1;i<=18*9;i++)
	{
		if (b == 1000000000000000000ll) ans = ans - work(a - 1, i);
		else ans+=work(b,i)-work(a-1,i);	
	}
	cout<<ans<<endl;
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章