CF331C3 解题报告
前言
鸽了不知道多久的题,好玩好玩好玩。
摘自杂题选做 Part 4. dp。
思路分析
首先来看 C1。
不难发现每次贪心的减掉最大的数位一定是最优的。
所以直接预处理出
然后来考虑 C2。
在 C2 中
感性的感觉一下,操作在
可以考虑数位 dp,不过这里介绍一种比较无脑的做法。
因为后面的很多部分都是相同的,所以我们直接把
考虑
在
考虑到数位只有
这个部分不难线性处理出。
然后来考虑对
在一次将
然后我们发现对于
所以我们只需要记录下来在
也就是设
那么我们的状态就是从
而
所以对
需要注意的点是当
然后是平衡复杂度的问题,总的复杂度为
比较难以理解,可以看一看代码。
首先来看 C3。
在 C3 中
其实我们注意到,因为你每次减的数字只有
所以这个
再附加上
具体的,我们把
那么对于
对于
根据上文所述,C3 比起 C2 新增的 map 里记忆化一下就好了。
代码也很相像,大概就是粘贴一遍再改一下。
复杂度
代码
#include<bits/stdc++.h>
#define int long long
#define mid ((l+r)>>1)
#define ls (p<<1)
#define rs (ls|1)
using namespace std;
const int N=1e6+10,M=1e5+10,V=2e2+10,lim=1e6,LIM=1e12,LLIM=1e18,mod=998244353,INF=0x3f3f3f3f3f3f3f3f;
int n,ans,mx[N],f[N][10],t[N][10];
map<pair<int,int>,pair<int,int>>mp;
namespace Fast_IO
{
static char buf[1000000],*paa=buf,*pd=buf,out[10000000];int length=0;
#define getchar() paa==pd&&(pd=(paa=buf)+fread(buf,1,1000000,stdin),paa==pd)?EOF:*paa++
inline int read()
{
int x(0),t(1);char fc(getchar());
while(!isdigit(fc)){if(fc=='-') t=-1;fc=getchar();}
while(isdigit(fc)) x=(x<<1)+(x<<3)+(fc^48),fc=getchar();
return x*t;
}
inline void flush(){fwrite(out,1,length,stdout);length=0;}
inline void put(char c){if(length==9999999) flush();out[length++]=c;}
inline void put(string s){for(char c:s) put(c);}
inline void print(int x)
{
if(x<0) put('-'),x=-x;
if(x>9) print(x/10);
put(x%10+'0');
}
inline bool chk(char c) { return !(c=='#'||c=='.'||c>='a'&&c<='z'||c>='A'&&c<='Z'||c>='0'&&c<='9'); }
inline bool ck(char c) { return c!='\n'&&c!='\r'&&c!=-1&&c!=' '; }
inline void rd(char s[],int&n)
{
s[++n]=getchar();
while(chk(s[n])) s[n]=getchar();
while(ck(s[n])) s[++n]=getchar();
n--;
}
}
using namespace Fast_IO;
inline pair<int,int> solve(int n,int mxx)
{
if(mp.count({n,mxx})) return mp[{n,mxx}];
int ans=0;if(n==1e12) n--,ans++;
int l=n/lim,r=n%lim;
while(l>=0)
{
int Mx=max(mx[l],mxx);
ans+=f[r][Mx];if(!l) break;
if(!t[r][Mx]) ans++,r=lim-Mx;
else r=lim+t[r][Mx];l--;
}int Mx=max(mx[l],mxx);
return mp[{n,mxx}]={ans,t[r][Mx]};
}
inline void solve()
{
n=read();for(int i=1;i<lim;i++) mx[i]=max(mx[i/10],i%10);
for(int j=0;j<10;j++) for(int i=1;i<lim;i++)
{
f[i][j]=f[max(0ll,i-max(mx[i],j))][j]+1;
if(i-max(mx[i],j)<0) t[i][j]=i-max(mx[i],j);
else t[i][j]=t[i-max(mx[i],j)][j];
}if(LLIM==n) n--,ans++;int l=n/LIM,r=n%LIM;
while(l>=0)
{
auto [a,b]=solve(r,mx[l]);ans+=a;if(!l) break;
if(!b) ans++,r=LIM-mx[l];else r=LIM+b;l--;
}print(ans);
}
signed main()
{
int T=1;
// T=read();
while(T--) solve();
genshin:;flush();return 0;
}