HDU - 4507 吉哥系列故事——恨7不成妻(数位dp)
題目鏈接:點擊查看
題目大意:給出閉區間[l,r],求出區間內與7不沾邊的數的平方和
與7有關的條件:
題目分析:拋去平方和,如果改成統計有多少個符合條件的數字,那就是傳統的數位dp了,只需維護一個dp[pos][sum1][sum2]即可,pos表示位數,sum1表示第二個條件的余數,sum2表示第三個條件的余數,向下傳遞的時候按照規則模擬即可,如果不懂的話看一眼代碼就好了
下面重點講一下如何處理平方和,關于維護數位dp中的平方和,我看到網上一個很巧妙的方法,需要維護三個值,分別是符合條件的數量,截止到當前位的符合條件的數值總和,截止到當前位的符合條件的平方和,為了方便傳遞,這里用一個結構體封裝一下。
那么下面先介紹參數變量,數量用cnt表示,總和用sum表示,平方和用powsum表示,然后按照傳統數位dp的思想,我們需要用到回溯法,從最高位先下傳到最低位然后回溯,隨后自底向上不斷傳遞狀態,那么我們可以假設兩個結構體:ans和temp,ans表示當前遞歸函數正在處理的數據,temp表示已經從底部返回上來的數據,即已經處理完成的數據,返回到ans中進一步處理。
如果不太理解ans和temp的關系,我再進一步說明一下,舉個例子,ans正在處理當前數位從0~9的數字,每個數字下傳后返回上來的結果,那么temp就表示每個數字下傳返回上來的結果,也就是說ans比temp高一級。
大概是這個樣子的一個關系
然后就是需要處理cnt,sum和powsum這三個參數該如何傳遞了
關于cnt,就是最基礎的數位dp的傳遞思路,只需要不斷累加就行,這里不過贅述,直接寫出轉移方程:
ans.cnt+=temp.cnt;
關于sum,我們首先可以先將當前表示的數字重構一下,表示為,i表示為最高位,x表示為剩余的低位部分,不過這只是表示的一個符合條件的數的sum,我們目前一共有cnt個數符合條件,所以這個結果需要乘以cnt,那么就顯而易見了,x就相當于已經處理完的上一級返回的sum的值了,想一想是不是?為了表示方便,我們用一個輔助數組p,來存儲每一位的進制,即p[1]=1,p[2]=10,p[3]=100以此類推,這樣就可以表示為
ans.sum+=(i*p[pos]+x)*temp.cnt;
化簡一下:
ans.sum+=i*p[pos]*temp.cnt+temp.sum;
還剩下一個平方和,這個的話需要在紙上化簡一下,是個完全平方公式,接著上面的重構的數字來說,平方和就是求這個結果,但直接求肯定不好求,需要化簡一下,展開平方后為:最后別忘了整體乘上cnt就好,那么初始的公式為:
ans.powsum+=(i*i*p[pos]*p[pos]+x*x+2*i*p[pos]*x)*temp.cnt;
把temp.cnt乘進去消掉x:
ans.powsum+=i*i*p[pos]*p[pos]*temp.cnt+temp.powsum+2*i*p[pos]*temp.sum;
這樣轉移方程就都有了,再說一下為什么temp.cnt與x相遇可以消去吧,因為我們一開始也提到了,temp.sum和temp.powsum指的是從temp中傳遞上來的結果,就是已經計算完畢的結果,已經乘過cnt了,而x表示的是上一層中關于一個可行數字的總和和平方和,所以這一層的x遇到cnt就可以用上一層的sum和powsum來替換了。
還有一點需要注意,在計算的過程中要實時取模,代碼中的具體實現是上述轉移方程穿插了無數個mod后的成品,及其難看,所以不用過多在意美觀性了。。在計算過程中cnt,sum和powsum都會爆int,所以記得開long long,最后就是輸出結果的時候,因為取模的原因,可能solve(b)比solve(a-1)要小,所以記得對負數取模:
(solve(b)-solve(a-1)+mod)%mod
上代碼了:
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=(1<<21)+100;struct Node {LL cnt,sum,powsum;//符合條件的個數,截止到目前為止的總和,截止到目前為止的平方和Node(){cnt=sum=powsum=0;} }dp[20][7][7];//dp[pos][sum%mod][mod]LL b[20];LL p[20];const int mod=1e9+7;Node dfs(int pos,int sum,int sum2,bool limit) {if(pos==0){Node temp;if(sum%7==0||sum2%7==0)temp.cnt=0;elsetemp.cnt=1;return temp;}if(!limit&&dp[pos][sum][sum2].cnt!=-1)return dp[pos][sum][sum2];int up=limit?b[pos]:9;Node ans;for(int i=0;i<=up;i++){if(i==7)continue;Node temp=dfs(pos-1,(sum+i)%7,(sum2*10+i)%7,limit&&i==b[pos]);ans.cnt=(ans.cnt+temp.cnt)%mod;ans.sum=(ans.sum+(p[pos]*i%mod*temp.cnt%mod)+temp.sum)%mod;ans.powsum=(ans.powsum+i*i*p[pos]%mod*p[pos]%mod*temp.cnt%mod+temp.powsum+2*i*p[pos]%mod*temp.sum%mod)%mod;}if(!limit)dp[pos][sum][sum2]=ans;return ans; }LL solve(LL n) {int cnt=0;while(n){b[++cnt]=n%10;n/=10;}return dfs(cnt,0,0,true).powsum; }int main() { // freopen("input.txt","r",stdin);int w;cin>>w;memset(dp,-1,sizeof(dp));p[1]=1;for(int i=2;i<20;i++)p[i]=p[i-1]*10%mod;while(w--){LL a,b;cin>>a>>b;cout<<(solve(b)-solve(a-1)+mod)%mod<<endl;}return 0; }?
總結
以上是生活随笔為你收集整理的HDU - 4507 吉哥系列故事——恨7不成妻(数位dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU - 3538 A sample
- 下一篇: HDU - 5242 Game(树形dp