数位平方和(rms2017模拟14-2)* * *【推理】

数位平方和(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)。
纯手画,丑勿喷,by 不可细说

代码
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;
} 







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值