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;
}