[COCI2008-2009#5] TRESNJA 解题记录
题意简述
每颗樱桃树都有一个编号,定义一颗樱桃树上的樱桃数量为所有连续的数字乘以长度的平方的和。
现给定区间
[
a
,
b
]
[a,b]
[a,b],求这个区间内的樱桃数量。
题目分析
看到这种数据范围自然而然地就会想到数位 DP。
不妨设
d
p
s
t
e
p
,
l
a
s
t
,
c
n
t
,
s
u
m
dp_{step,last,cnt,sum}
dpstep,last,cnt,sum 表示当前填到第
s
t
e
p
step
step 位,上一位数字是
l
a
s
t
last
last,当前连续数字的个数是
c
n
t
cnt
cnt,当前编号的樱桃数量是
s
u
m
sum
sum。
对于枚举的每一位,考虑填
0
∼
l
i
m
i
t
0 \sim limit
0∼limit,其中
l
i
m
i
t
limit
limit 为当前为的上界。更新的时候如果当前选的数字等于
l
a
s
t
last
last,那么
c
n
t
+
1
cnt+1
cnt+1,
a
n
s
ans
ans 不变,否则更新
a
n
s
=
a
n
s
+
l
a
s
t
×
c
n
t
2
ans=ans+last \times cnt^2
ans=ans+last×cnt2。使用记忆化搜索。
注意:如果你 #define int long long
一定要注意空间限制。
AC Code
#include<bits/stdc++.h>
#define arrout(a,n) rep(i,1,n)std::cout<<a[i]<<" "
#define arrin(a,n) rep(i,1,n)std::cin>>a[i]
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define dep(i,x,n) for(int i=x;i>=n;i--)
#define erg(i,x) for(int i=head[x];i;i=e[i].nex)
#define dbg(x) std::cout<<#x<<":"<<x<<" "
#define mem(a,x) memset(a,x,sizeof a)
#define all(x) x.begin(),x.end()
#define arrall(a,n) a+1,a+1+n
#define PII std::pair<int,int>
#define m_p std::make_pair
#define u_b upper_bound
#define l_b lower_bound
#define p_b push_back
#define CD const double
#define CI const int
#define int long long
#define il inline
#define ss second
#define ff first
#define itn int
CI N=16;//卡空间
int a,b,num[N],dp[N][N][N][2005];
int dfs(int step,int last,int cnt,int sum,int limit) {
if(step>num[0]) {
return sum+last*cnt*cnt;
}
if(!limit&&dp[step][last][cnt][sum]!=-1) {
return dp[step][last][cnt][sum];
}
int up=limit?num[num[0]-step+1]:9;
int s=0;
rep(i,0,up) {
s+=dfs(step+1,i,i==last?cnt+1:1,i==last?sum:sum+cnt*cnt*last,limit&&i==up);
}
if(!limit) {
dp[step][last][cnt][sum]=s;
}
return s;
}
int solve(int x) {
num[0]=0;
while(x) {
num[++num[0]]=x%10;
x/=10;
}
mem(dp,-1);
return dfs(1,-1,0,0,1);
}
signed main() {
std::cin>>a>>b;
std::cout<<solve(b)-solve(a-1);
return 0;
}