zoukankan      html  css  js  c++  java
  • J

    J - 吉哥系列故事――恨7不成妻

    单身!
      依然单身!
      吉哥依然单身!
      DS级码农吉哥依然单身!
      所以,他生平最恨情人节,不管是214还是77,他都讨厌!
      
      吉哥观察了214和77这两个数,发现:
      2+1+4=7
      7+7=72
      77=7
    11
      最终,他发现原来这一切归根到底都是因为和7有关!所以,他现在甚至讨厌一切和7有关的数!

    什么样的数和7有关呢?

    如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
      1、整数中某一位是7;
      2、整数的每一位加起来的和是7的整数倍;
      3、这个整数是7的整数倍;

    现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。

    Input

    输入数据的第一行是case数T(1 <= T <= 50),然后接下来的T行表示T个case;每个case在一行内包含两个正整数L, R(1 <= L <= R <= 10^18)。

    Output

    请计算[L,R]中和7无关的数字的平方和,并将结果对10^9 + 7 求模后输出。

    Sample Input

    3
    1 9
    10 11
    17 17

    Sample Output

    236
    221
    0

    参考博客:http://www.cnblogs.com/kuangbin/archive/2013/05/01/3053233.html

    分析 :

    当然,看完博客还是懵逼,因为我一开始的思路是错的。

    首先这道题的状态是很好想的

    dp[pos][Pre_sum][Pre_Product%7]dp[pos][Pre\_sum][Pre\_Product\%7] 代表未知的pos位的前缀十进制位之和取余7为Pre_sum,前缀的值取余7为Pre_Product。

    举个例子:54****对应的状态是dp[4][9%7][54%7]dp[4][9\%7][54\%7]

    这个地方是我错的原因,不想看的可以跳过。

    然后就开始数位dp加递归求解了吗?,合并一下就像这样?

    ans+=dfs(pos-1,(PreSum+i)%7,PreProduct*10+i,istop&&i==endd);//这个合并的方法是错的
    
    

    我们求的答案是满足条件的数的平方和,所以在进行状态合并的时时候相同状态对应的前缀可能是不同的,所以他们呢的平方和也是不同的,不能直接加上次计算结果。

    就像23**与72**,对应的状态是一样的(先不考虑数位上存在7的条件),但是前者计算过了,在计算后者不能直接加前者计算的值。

    所以我们应该寻求一个新的解法!

    想一想,如果我们知道该状态下后pos位满足条件的数都是什么,我们应该怎么计算呢?

    假如说第pos位是i,后pos-1位满足条件的数都已经知道有n个,对应的数字分别为m1,m2,m3… mn

    那么后pos位中满足条件的平方和我们可以计算出来(这里的平方和中的数不包括前缀)。

    设k位第pos位对应的权值k=pow(10,pos1)k=pow(10,pos-1)

    (ik+mj)2==(ik)2+mj2+2ikmj(i*k+m_j)^2==(i*k)^2+m_j^2+2*i*k*m_j

    枚举这n个数在第pos为i的情况下,在该状态下后pos位满足条件的数的平方和为

    Product=n(ik)2+j=1nmj2+2ikj==1nmjProduct=n*(i*k)^2+sum_{j=1} ^nm_j^2+2*i*k*sum_{j==1}^nm_j

    枚举第pos位就得到后pos位满足条件的数的平方和,在上面的公式中用到了n(改状态下满足条件的数的个数),sum(该状态下满足条件的数的和),Preduct(改状态下满足条件的数的平方和)。

    在合并的时候需要把这三个值全都统计一下,以便递归求解,最后的答案在Preduct里面。

    #include<cstdio>
    #include<algorithm>
    #include<stack>
    #include<queue>
    #include<map>
    #include<cmath>
    #include<vector>
    #include<cstring>
    #include<string>
    #include<iostream>
    #include<iomanip>
    #define mset(a,b)   memset(a,b,sizeof(a))
    using namespace std;
    typedef unsigned long long ull;
    typedef long long ll;
    const int maxn=1e5+10;
    const int branch=26;
    const int inf=0x3f;
    const ll MOD=1e9+7;
    struct node{//(针对于后pos位未知位数的)
    ll cnt;//满足条件的个数
    ll sum;//满足条件的数的和
    ll Product;//Product为满足条件的数的平方和
    node(){
    cnt=-1;
    sum=Product=0ll;
    }
    node(ll a,ll b,ll c){
    cnt=a,sum=b,Product=c;}
    };
    node dp[25][10][10];//dp[pos][Pre_sum][Pre_Product%7];
    int num[25];
    ll quick_pow(ll a,ll b)
    {
        ll ans=1;
        while(b)
        {
            if(b&1)
                ans=(ans*a)%MOD;
            a=(a*a)%MOD;
            b>>=1;
        }
        return ans;
    }
    node dfs(int pos,int PreSum,int PreProduct,bool istop)
    {
        if(!pos)
        {
            if(PreSum%7!=0&&PreProduct%7!=0)
                return node(1,0,0);
            else
                return node(0,0,0);
        }
        if(!istop&&dp[pos][PreSum%7][PreProduct%7].cnt!=-1)
            return dp[pos][PreSum%7][PreProduct%7];
        int endd=istop?num[pos]:9;
        node ans(0,0,0);
        ll powval=quick_pow(10ll,pos-1);
        for(int i=0;i<=endd;++i)
        {
            if(i==7)
                continue;
            //针对于每个位
            node mid=dfs(pos-1,(PreSum+i)%7,(PreProduct*10+i)%7,istop&&i==endd);
            ll k=i*powval%MOD;
            ans.cnt+=mid.cnt;
            ans.sum+=(mid.sum+(mid.cnt*k)%MOD)%MOD;
            ans.Product+=(mid.Product+(2*k*mid.sum)%MOD+((mid.cnt*k)%MOD)*(k%MOD))%MOD;
            ans.cnt%=MOD;
            ans.sum%=MOD;
            ans.Product%=MOD;
        }
        if(!istop)
            dp[pos][PreSum%7][PreProduct%7]=ans;
        return ans;
    }
    ll calc(ll val)
    {
        int pos=0;
        do{
            num[++pos]=val%10;
            val/=10;
        }
        while(val);
        return dfs(pos,0,0,1).Product;
    }
    int main()
    {
        int t;
        ll a,b;
        mset(dp,-1);
        scanf("%d",&t);
        while(t--)
        {
            scanf("%I64d%I64d",&a,&b);
            printf("%I64d
    ",(((calc(b)-calc(a-1))%MOD)+MOD)%MOD);
        }
        return 0;
    }
    
  • 相关阅读:
    值类型和引用类型
    0513二分查找练习
    0512随机4位验证码
    0511java 随机6个不同的彩票数
    随机数的产生机制
    0510Java 练习
    0509java练习题
    java循环作业
    字符集的由来及发展
    hdu2577_键入字母
  • 原文地址:https://www.cnblogs.com/dchnzlh/p/10427255.html
Copyright © 2011-2022 走看看