【HDU4507】恨7不成妻(数位DP)

点此看题面

大致题意: 让你求出一段区间内与\(7\)无关的数的平方和。与\(7\)无关的数指整数中任意一位不为\(7\)整数的每一位加起来的和不是\(7\)的整数倍这个整数不是\(7\)的倍数


数位\(DP\)

这题应该比较显然是一道 数位\(DP\) 题。


如何记录状态

这道题关键就在于如何记录状态,其余的就和普通的数位\(DP\)差不多了。

我们可以用\(f_{x,s1,s2}\)来表示还剩\(x\)位,这个数除末\(x\)位以外模\(7\)余\(s1\),这个数每一位之和除末\(x\)位以外模\(7\)余\(s2\)时所有与\(7\)无关的数的末\(x\)位的平方和。

但是,如果光光记录平方和,转移就有点困难了。

所以,我们先要来一点恶心的数学转化。


数学转化

让我们来研究一下\((x_1+t*10^y)^2+(x_2+t*10^y)^2+...+(x_n+t*10^y)^2\)这个式子。

先由完全平方公式可得:

\[原式=(x_1^2+2*x_1*t*10^y+10^{2y})+(x_2^2+2*x_2*t*10^y+10^{2y})+...+(x_n^2+2*x_n*t*10^y+10^{2y})\]

然后,我们将其去括号并重新组合,可得:

\[原式=(x_1^2+x_2^2+...+x_n^2)+2*t*10^y*(x_1+x_2+...+x_n)+(t*10^y)^2*n\]

如果用\(f(n)\)来表示\(x_1+x_2+...+x_n\),\(f^2(n)\)来表示\(x_1^2+x_2^2+...+x_n^2\),则:

\[原式=f^2(n)+2*t*10^y*f(n)+(t*10^y)^2*n\]

我们可以预处理出\(10^y\),并对于每个状态记录下\(n,f(n)\)和\(f^2(n)\),这样就可以实现\(O(1)\)转移了。


状态转移方程

用\(ns1\)来表示\((s1*10+i)\)%\(y\),\(ns2\)来表示\((s2+i)\)%\(y\)。

\[n_{x,s1,s2}=\sum_{i=0}^{lim}n_{x-1,ns1,ns2}\]

\[f_{x,s1,s2}=\sum_{i=0}^{lim}f_{x-1,ns1,ns2}+n_{x-1,ns1,ns2}*i*10^{x-1}\]

\[f^2_{x,s1,s2}=\sum_{i=0}^{lim}f^2_{x-1,ns1,ns2}+2*i*10^x*f_{x-1,ns1,ns2}+(i*10^{x-1})^2*n_{x-1,ns1,ns2}\]


代码

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define INF 1e9
#define Inc(x,y) ((x+=y)>=MOD&&(x-=MOD))
#define MOD 1000000007
using namespace std;
LL n,m;
class FIO
{
    private:
        #define Fsize 100000
        #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
        #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
        LL f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
    public:
        FIO() {FinNow=FinEnd=Fin;}
        inline void read(LL &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));x*=f;}
        inline void read_char(char &x) {while(isspace(x=tc()));}
        inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
        inline void write(LL x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
        inline void write_char(char x) {pc(x);}
        inline void write_string(string x) {register LL i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
        inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
class Class_DigitalDP
{
    private:
        #define ten(x) ((x<<3)+(x<<1))
        LL ans,len,num[20],tn[20];
        struct key//记录一个状态
        {
            LL res,res_,tot;//res记录平方和,res_记录和,tot记录个数
            key(LL x=0,LL y=0,LL z=-1):res(x%MOD),res_(y%MOD),tot(z%MOD){}
        }f[20][7][7];
        inline void Init(LL x) {len=0;while(x) num[++len]=x%10,x/=10;num[len+1]=0;}
        inline key dfs(LL x,LL s1,LL s2,LL flag)
        {
            register LL i,lim=9,k;register key w=key(0,0,0),t;//w记录结果
            if(!x) return key(0,0,s1&&s2);//如果x为0,返回结果
            if(flag&&~f[x][s1][s2].tot) return f[x][s1][s2];//如果当前状态肯定在求解范围内,且已经访问并求解过当前状态,返回上次求解得到的答案
            if(!flag) (num[x]^7&&(t=dfs(x-1,(ten(s1)+num[x])%7,(s2+num[x])%7,0),k=num[x]*tn[x-1]%MOD,w=key(t.res+(t.res_<<1)*k%MOD+t.tot*k%MOD*k%MOD,t.res_+t.tot*k%MOD,t.tot),true)),lim=num[x]-1;//对于不一定在求解范围内的值特殊处理
            for(i=0;i<=lim;++i) if(i^7) t=dfs(x-1,(ten(s1)+i)%7,(s2+i)%7,1),k=i*tn[x-1]%MOD,w=key(w.res+t.res+(t.res_<<1)*k%MOD+t.tot*k%MOD*k%MOD,w.res_+t.res_+t.tot*k%MOD,w.tot+t.tot);//对每一个不为7的数字进行状态转移
            if(flag) f[x][s1][s2]=w;//如果当前状态肯定在求解的范围内,就将求解出的答案记录下来,实现记忆化
            return w;//返回求解出的答案
        }
    public:
        Class_DigitalDP() {tn[0]=1;for(register LL i=1;i<20;++i) tn[i]=ten(tn[i-1])%MOD;}//预处理出10的幂
        inline LL GetAns(LL x) {return !x?0:((void)(Init(x)),dfs(len,0,0,0).res);}//求解答案
}DigitalDP;
int main()
{
    register LL i;register LL T;F.read(T);
    while(T--) F.read(n),F.read(m),F.write(((DigitalDP.GetAns(m)-DigitalDP.GetAns(n-1))%MOD+MOD)%MOD),F.write_char('\n');
    return F.end(),0;
}

原文地址:https://www.cnblogs.com/chenxiaoran666/p/HDU4507.html

时间: 2024-10-07 12:40:01

【HDU4507】恨7不成妻(数位DP)的相关文章

hdu-4507 吉哥系列故事——恨7不成妻 数位DP 状态转移分析/极限取模

http://acm.hdu.edu.cn/showproblem.php?pid=4507 求[L,R]中不满足任意条件的数的平方和mod 1e9+7. 条件: 1.整数中某一位是7:2.整数的每一位加起来的和是7的整数倍:3.这个整数是7的整数倍: 首先想到数位DP,我们看下如何维护. 最基本的dp需要两维来维护起始数字和长度,此外对于数位求和mod 7的余数需要一维来维护,对于一个数mod 7的余数需要一维维护. 此外我们处理一下平方和,对于一个x开头,长度为len的xoo型数集,把它分成

HDU 4507 吉哥系列故事――恨7不成妻(数位dp&amp;好魔性的一道好题)

题目链接:[kuangbin带你飞]专题十五 数位DP J - 吉哥系列故事――恨7不成妻 题意 Time Limit:500MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Description 单身! 依然单身! 吉哥依然单身! DS级码农吉哥依然单身! 所以,他生平最恨情人节,不管是214还是77,他都讨厌! 吉哥观察了214和77这两个数,发现: 2+1+4=7 7+7=7*2 77=7*11 最终,他发现原来这一切归根到底都是

Hdu 4507 吉哥系列故事——恨7不成妻 (数位DP)

题目链接: Hdu 4507 吉哥系列故事——恨7不成妻 题目描述: 中文题面不描述. 解题思路: 从数据范围可看出是数位DP.根据题目给的限制,如果是求满足限制的数的数目的话,就很简单了.但是这个题目是让求满足题目中限制的数的平方和.我们可以求出区间中满足题目中限制的数的数目,和这些数的和,然后从这两个东西推出这些数的平方和. 假设现在我们已经递归出后面的x-1位满足题目限制的数的数目(num),和(sum),平方和(ssum),当前x位枚举为i,就可以推出当前节点改变为Num += num,

HDU ACM 4507 吉哥系列故事——恨7不成妻 -&gt;数位DP

题意:如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关-- 1.整数中某一位是7: 2.整数的每一位加起来的和是7的整数倍: 3.这个整数是7的整数倍: 现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和. 分析:数位DP,关键内容如下. (pre0+i)%7用于处理各个位数之和时候为7的倍数,(pre1*10+i)%7用于处理这个数是否为7的倍数. #include<iostream> using namespace std; const __int64 mod=100

hdu4507吉哥系列故事——恨7不成妻 数位dp

//(a1 + a2 + a3 + a4)^2 + (b1 + b2 + b3 + b4)^2 //=a1^2 + (a2+a3+a4)^2 + 2*a1*(a2+a3+a4) + b1^2 + 2*b1*(b1+b2+b3) //每次dfs返回三个数,a,b,c //a表示个数,b表示所有数的和,c表示所有数的平方和 //那么更新的ans.a += a; //ans.b +=(b + i*temp_b*a) //ans.c+=(c + (temp_b*i)^2*a + 2*(temp_b*i)

hdu 4507 吉哥系列故事――恨7不成妻 数位dp

题意:中文题. 思路:设dp[pos][i][j]表示当前考虑pos位,之前的数位和对7的余数为i,之前的数值对7的余数为j,与之后的(pos+1)位组合满足条件 的状态(包括之后(pos+1)位满足的个数,后缀和sum,后缀平方和),详见代码: /********************************************************* file name: hdu4507.cpp author : kereo create time: 2015年02月10日 星期二

HDU4507 吉哥系列故事——恨7不成妻 题解 数位DP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4507 题目大意: 找到区间 \([L,R]\) 范围内所有满足如下条件的数的 平方和 : 不包含'7': 不能被 7 整除: 各位之和不能被 7 整除. 注意:求的是满足条件的数的 平方和 ! 解题思路: 使用 数位DP 尽情求解. 但是因为这里求的是满足要求的元素的平方和,而不是元素的个数. 所以我们不能简单地开long long来存放结果, 而是开一个结构体来存放结果,结构体中需要包含三个元素:

HDU - 4507 吉哥系列故事――恨7不成妻 (数位DP)

Description 单身! 依然单身! 吉哥依然单身! DS级码农吉哥依然单身! 所以,他生平最恨情人节,不管是214还是77,他都讨厌! 吉哥观察了214和77这两个数,发现: 2+1+4=7 7+7=7*2 77=7*11 最终,他发现原来这一切归根到底都是因为和7有关!所以,他现在甚至讨厌一切和7有关的数! 什么样的数和7有关呢? 如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关―― 1.整数中某一位是7: 2.整数的每一位加起来的和是7的整数倍: 3.这个整数是7的整数

HDU 4507 吉哥系列故事——恨7不成妻 (数位DP)

题意: 如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关: 1.整数中某一位是7: 2.整数的每一位加起来的和是7的整数倍: 3.这个整数是7的整数倍: 给定一个区间[L,R],问在此区间内和7无关的所有数字的平方和. 思路: 第一步好解决,只是数位DP的基础.第二步是十进制的所有位加起来是7的整数倍,这个只是需要用多一维来记录%7的结果就行了.第三步是7的整数倍问题,假设c=a+b,那么c%7=(a%7+b)%7,就假设这个数是10086,那么(10000%7+86)%7就行了,