【洛谷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;//输出答案
}
待到再迷茫时回头望,所有脚印会发出光芒