数位平方和(count.cpp)
试题描述:
定义 S(n)表示 n 的各个数位的 k 次方的和。 定义H(n) = min{n,S(n),H(S(n)) } 求∑ H[i] mod 10000007
B i=A
输入格式:
一行三个数K、A、B
输出格式:
一个数∑ H[i] mod 10000007
B i=A
输入样例:
2 1 5
输出样例: 1
4
数据规模:
对于 20%的数据,满足 1<= A,B<=50;
对于 100%的数据,满足 1<= A,B<=106,K<=6。
题解:
通过定义式,可以发现这是一个递归式,用递归求解。
由这个递归式的定义可以发现,这个图一定是由环 + 一些外向树构成的,需要记忆化搜索。记忆化搜索时,有一点需要注意,一个在环中的数u1出发的一个序列u1→u2→u3→…→uk→uk+1→…→u1,这个环的最优解是uk。当按顺序DFS下去时,当第二次遇到u1的时候,此时并不知道uk,因为uk还在递归的堆栈中,但u1已经是第二次遇到了,所以就直接返回了,这样的结果就是S[u1] = S[u2] = … = uk;S[uk+1] 、S[uk+2],…就都无法得到最优解了。
处理的方法就是在递归的时候,判断一个数如果是第3次遇到才直接返回,这样就保证了最优值在环上至少走一圈。也可以不用记忆化搜索,先找出所有的圈,求出圈上的最优解,然后沿外向树往下更新。
时间复杂度:O(B)。
空间复杂度:O(B)。
代码
std真是妙啊,改成了自己的风格
#include<bits/stdc++.h>
#define F( i,a,b ) for( int i=(a);i<=(b);i++ )
#define F_2( i,a,b ) for( int i=(a);i>=(b);i-- )
#define N 10000008
#define M 10001
#define P 10000007
#define LL long long
#define oo 0x7fffffff
using namespace std;
inline int read()
{
int f=1,s=0;
char ch=getchar();
while( ch>'9' || ch<'0' ) { if( ch=='-' ) f=-1; ch=getchar(); }
while( ch<='9' && ch>='0' ) { s=( s<<1 )+( s<<3 )+ch-'0'; ch=getchar(); }
return f*s;
}
int m,n,k;
int K,A,B;
int tot,ans,cnt;
int ks[11],h[N],lst[N];
bool vis[N];
int S( int x )
{
int y=0;
while( x )
{
y=( y+ks[x%10] )%P;
x/=10;
}
return y;
}
int H( int x )
{
if( h[x] ) return h[x];
int tx=x,t1=oo,t2=oo;
cnt=0;
while( !vis[x] ) // 找环
{
cnt++;
lst[cnt]=x;
vis[x]=true;
x=S( x );
}
int j=0;
F( i,1,cnt ) // 找到由x出发的环和外向树
{
if( x==lst[i] ) //有新环
j=i;
if( j>0 )
t2=min( t2,lst[i] );
}
if( j==0 ) //无新环
{
t1=h[x]; // 更新外向树上的解
F_2( i,cnt,1 )
{
t1=min( t1,lst[i] );
h[lst[i]]=t1;
}
}
else //有新环
{
F( i,j,cnt ) // 更新环上的解
{
h[lst[i]]=t2;
}
t1=t2;
F_2( i,j-1,1 ) // 更新环之外的外向树的解
{
t1=min( t1,lst[i] );
h[lst[i]]=t1;
}
}
return h[tx];
}
int main()
{
freopen( "count.in","r",stdin );
freopen( "count.out","w",stdout );
K=read();
A=read();
B=read();
F( i,0,9 ) //初始化i^k
{
ks[i]=1;
F( j,1,K )
ks[i]*=i;
}
F( i,A,B )
{
ans+=H( i );
ans%=P;
}
cout<<ans<<endl;
return 0;
}