【洛谷5128】好時光(數位DP)
阿新 • • 發佈:2020-09-12
大致題意: 求\(\sum_{i=1}^N f(i,k)\),其中\(f(i,k)\)定義為將十進位制數\(i\)轉化為\(k\)進位制並按位寫成一個數列的形式,其中最長的等差序列子串的長度。
前言
昨天晚上懶了點沒拉蚊帳,結果半夜被蚊子鬧醒不知道多少次。
於是今天就不太想寫題,口胡了好幾題都懶得去碼。
突然發現一個上午要過完了,最終決定隨機跳道題認真做做,然後就找到了這道題。。。
數位\(DP\)
感覺這道題數位\(DP\)還是挺容易推的,只不過時間、空間卡得有點緊。
首先我們把題目中給定的\(N\)轉化成\(k\)進位制數,然後設\(f_{n,x,y,u,v}\)表示從左往右\(DP\) 到右起第\(n\)位、上上個數是\(x\)、上個數是\(y\)、當前等差序列長度為\(u\)、最長等差序列長度為\(v\)的答案總和。
轉移只要列舉一個範圍內的數,然後判斷是否能與\(x,y\)成等差序列即可,細節也不多。
程式碼
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 18 #define K 60 #define X 19260821 using namespace std; int n,k,a[500],p[N+5],f[N+1][K][K][N+1][N+1];char s[500]; I int DP(CI n,CI x,CI y,CI u,CI v,CI fg,CI g=0)//數位DP { if(!n) return v;if(fg&&~f[n][x][y][u][v]) return f[n][x][y][u][v];//記憶化 RI i,res=0,t=fg?k-1:p[n];for(i=0;i<=t;++i)//列舉一個範圍內的數 res+=i-y==y-x?DP(n-1,y,i,u+1,max(v,u+1),fg||i<p[n]):DP(n-1,y,i,2,v,fg||i<p[n]);//分類討論 End:return res%=X,fg&&(f[n][x][y][u][v]=res),res; } int main() { RI i,l=1,r,x,y;for(scanf("%s%d",s+1,&k),r=strlen(s+1),i=1;i<=r;++i) a[i]=s[i]&15;W(l<=r) { for(x=0,i=l;i<=r;++i) a[i]=(x=x*10+a[i])/k,x%=k;p[++n]=x;W(l<=r&&!a[l]) ++l;//進位制轉化 } RI res=n==1?p[1]:k-1;for(memset(f,-1,sizeof(f)),i=n;i^1;--i) for(x=1;x<=(i==n?p[n]:k-1);++x)//列舉最高非零位防止出現前導0 for(y=0;y<=(i==n&&x==p[n]?p[n-1]:k-1);++y) res=(res+DP(i-2,x,y,2,2,i^n||x^p[n]||y^p[n-1]))%X;//列舉x,y表示開頭兩數 return printf("%d\n",res),0;//輸出答案 }