HDU ~ 4507 ~ 吉哥系列故事――恨7不成妻 (数位DP + 完全平方公式)

思路

依旧是数位DP, 我们需要过程中需要维护数字num%7和每一位的和sum%7。

所以定义DP[pos][mod1][mod2]表示选到pos位置num%7=mod1和sum%7=mod2的答案。

如果答案是要数字个数就好办了,但是答案要的是平方和。如果第二位我们去存当前选的数字num,而不是num%7,我们内存会炸,不可行。

大家应该还记得完全平方公式吧,即(a+b)^2=a^2+2ab+b^2,假设要求一个两位数x的答案,假设第一位为pre,第二位为t,那么x = pre*10 + t,所有符合要求的 x 的平方和怎么求呢?首先第一位选可行数字 i 的情况下,第二位选一个可行的值 t ,答案就需要加上此时选的数平方即(i*10+t)^2=i^2*10^2+2*i*10*t +t^2。如果对于 i 的情况有多个可行的 t

,那么答案就是cnt\cdot(i^2*10^2)+2*i*10*\sum t+\sum t^2。推广到选多位数字的时候一样。

DP数组需要维护三个值,cnt 表示当前状态下后面随便选的可行数字个数,sum表示后面位的 \sum t,sqsum表示 \sum t^2

假设选到pos位置,当前选了 i 元素,深搜得到的dp值为 t 。

cnt 的维护比较简单,一路加过来就好了,ans.cnt \quad += \quad t.cnt

sum的维护,ans.sum \quad += \quad t.sum + t.cnt*i*10^{pos}

sqsum(平方和)的维护,ans.sqsum += t.cnt*(i*10^{pos})^2+2* i*10^{pos}* t.sum+t.sqsum

然后注意下mod和long long即可。

因为取模的原因,最后R -(L-1)可能为负数所以要+mod在取余。

 

#include<bits/stdc++.h>
using namespace std;
const int MOD = 1e9+7;
typedef long long LL;
struct DP
{
    LL cnt, sum, sqsum;
    DP () {}
    DP (LL cnt, LL sum, LL sqsum): cnt(cnt), sum(sum), sqsum(sqsum) {}
}dp[20][10][10];
LL ten[20];
string s;
DP dfs(int pos, int mod1, int mod2, bool limit)
{
    if (pos == -1) return DP(mod1!=0 && mod2!=0, 0, 0);
    if (!limit && dp[pos][mod1][mod2].cnt != -1) return dp[pos][mod1][mod2];
    int E = limit?s[pos]-'0':9;
    DP ans = DP(0, 0, 0);
    for (int i = 0; i <= E; i++)
    {
        if (i == 7) continue;
        DP t = dfs(pos-1, (mod1+i)%7, (mod2*10+i)%7, limit&&(i==E));
        ans.cnt += t.cnt;
        ans.cnt %= MOD;

        ans.sum += t.sum + t.cnt*i%MOD*ten[pos]%MOD;
        ans.sum %= MOD;

        ans.sqsum += t.cnt*i*i%MOD*ten[pos]%MOD*ten[pos]%MOD;
        ans.sqsum += t.sqsum + 2*i*ten[pos]%MOD*t.sum%MOD;
        ans.sqsum %= MOD;
    }
    if (!limit) dp[pos][mod1][mod2] = ans;
    return ans;
}
LL solve(LL x)
{
    s = to_string(x); reverse(s.begin(), s.end());
    return dfs(s.size()-1, 0, 0, 1).sqsum;
}
int main()
{
    ten[0] = 1;
    for (int i = 1; i < 20; i++) ten[i] = (ten[i-1]*10)%MOD;
    memset(dp, -1, sizeof(dp));
    int T; scanf("%d", &T);
    while (T--)
    {
        LL L, R; scanf("%lld%lld", &L, &R);
        printf("%lld\n", (solve(R)-solve(L-1)+MOD)%MOD);
    }
    return 0;
}
/*
3
1 9
10 11
17 17
*/

 

 

 

源码来自:https://pan.quark.cn/s/fdd21a41d74f 正方教务管理系统成绩推送 简介 使用本项目前: 早晨睡醒看一遍教务系统、上厕所看一遍教务系统、刷牙看一遍教务系统、洗脸看一遍教务系统、吃早餐看一遍教务系统、吃午饭看一遍教务系统、睡午觉前看一遍教务系统、午觉醒来看一遍教务系统、出门前看一遍教务系统、吃晚饭看一遍教务系统、洗澡看一遍教务系统、睡觉之前看一遍教务系统 使用本项目后: 成绩更新后自动发通知到微信 以节省您宝贵的时间 测试环境 正方教务管理系统 版本 V8.0、V9.0 如果你的教务系统页面与下图所示的页面完全一致或几乎一致,则代表你可以使用本项目。 目前支持的功能 主要功能 每隔 30 分钟自动检测一次成绩是否有更新,若有更新,将通过微信推送及时通知用户。 相较于教务系统增加了哪些功能? 显示成绩提交时间,即成绩何时被录入教务系统。 显示成绩提交人姓名,即成绩由谁录入进教务系统。 成绩信息按时间降序排序,确保最新的成绩始终在最上方,提升用户查阅效率。 计算 计算百分制 对于没有分数仅有级别的成绩,例如”及格、良好、优秀“,可以强制显示数字分数。 显示未公布成绩的课程,即已选课但尚未出成绩的课程。 使用方法 Fork 本仓库 → 开启 工作流读写权限 → → → → → 添加 Secrets → → → → → → Name = Name,Secret = 例子 程序会自动填充 尾部的 ,因此你无需重复添加 对于部分教务系统,可能需要在 中添加 路径,如: 开启 Actions → → → 运行 程序 → → 若你的程序正常运行且未报错,那么在此之后,程序将会每隔 30 分钟自动检测一次成绩是否有更新 若你看懂上述使用...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值