HDU 6156 題解
阿新 • • 發佈:2021-06-21
HDU 6156 題解
題目分析
設 \(cnt\) 表示在 \(k\) 進位制下,\([L,R]\) 範圍內迴文數的個數。故有 (R-L+1)-cnt
個數字不是迴文數。根據題意,在 \(k\) 進位制下對答案的貢獻為 cnt*k+(R-L+1)-cnt
。
不難想到用數位 DP 去求出 \(cnt\) 的值。
設計狀態 \(dp[pos][len][k]\),表示當前填到第 \(pos\) 位,總長度為 \(len\),進位制數位 \(k\) 下的數的個數。
Q1 :為何將 \(len\) 設計入狀態?
對於 \(k\) 進位制下的數 \(x\),必然沒有前導零。因此判斷它是否為迴文數,就看這個長度為 \(len\)
Q2 :如何在函式中下傳 \(len\) ?
\(len\) 的值與前導零密切相關。如果前面為前導零,且填的這一位仍為 \(0\),那麼接下來 dfs 的數的長度就 -1。否則,\(len\) 的值就固定了。
Q3 :如何方便地判斷是否為迴文串?
- 如果填的為前導零,無需判斷,繼續搜。
- 如果當前已填的長度小於總長 \(len\) 的一半,則可以任意填 \(0-9\) 間的任意數字,畢竟不影響是否迴文。
- 如果當前已填的長度大於總長 \(len\) 的一半,那麼填的數就已經固定了,為之前填過的關於 \(\dfrac{len+1}{2}\) 位對稱的數字。(\(len\) 為偶數時假想最中間有一位)
Q4 :你怎麼知道前面填了什麼數?
由於深搜是一條路徑往黑裡搜,當前所搜的狀態之前的東西是暫時不會改變的,因此開一個數組記錄當前已填的每位所對應所填入的數字即可。
程式碼展示
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int M=80; int k,a[M],c[M]; ll dp[M][M][40]; char s[M]; ll dfs(int pos,int len,int k,bool lead,bool limit) { if(pos==0)return 1; if(!limit&&dp[pos][len][k]!=-1) return dp[pos][len][k]; int up=limit?a[pos]:k-1; ll ans=0; for(int i=0;i<=up;i++) { ll tmp=0;c[pos]=i; if(lead&&(i==0)) tmp=dfs(pos-1,len-1,k,true,limit&&i==up); else if(pos>len/2) tmp=dfs(pos-1,len,k,false,limit&&i==up); else if(c[len-pos+1]==i) tmp=dfs(pos-1,len,k,false,limit&&i==up); ans=ans+tmp; } if(!limit)dp[pos][len][k]=ans; return ans; } ll solve(ll x,int k) { int len=0; while(x) { a[++len]=x%k; x/=k; } return dfs(len,len,k,true,true); } int main() { memset(dp,-1,sizeof(dp)); int T;scanf("%d",&T); for(int t=1;t<=T;t++) { ll L,R,ans=0;int l,r; scanf("%lld%lld%d%d",&L,&R,&l,&r); for(int i=l;i<=r;i++) { ll sum=solve(R,i)-solve(L-1,i); ans+=sum*i; ans+=(R-L+1-sum); } printf("Case #%d: %lld\n",t,ans); } return 0; }