1. 程式人生 > 實用技巧 >【洛谷5128】好時光(數位DP)

【洛谷5128】好時光(數位DP)

點此看題面

大致題意:\(\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;//輸出答案
}