hdu 4507 吉哥系列故事——恨7不成妻

吉哥系列故事——恨7不成妻

1

题意

一个正整数和 7 7 7 有关当且仅当满足以下条件之一

  • 数位中某一位是 7 7 7
  • 数位和能被 7 7 7 整除
  • 这个整数能被 7 7 7 整除

统计 [ l , r ] [l,r] [l,r] 内所有和 7 7 7 无关 的数字的 平方和

思路

这道题需要一点思维。我们先来看一个例子:

如果我们现在处理枚举 p o s pos pos 位为 p p p,低 p o s − 1 pos - 1 pos1 位的结果已经处理完了,在当前限制下,低 p o s − 1 pos - 1 pos1 的与 7 7 7 无关的数有: x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3 三个的话,那么它们的平方和应该是: x 1 2 + x 2 2 + x 3 2 x_1 ^ 2 + x_2 ^ 2 + x_3 ^ 2 x12+x22+x32,现在我们要加上 p p p 的贡献,就是将 p p p 拼接上去, p p p 这一位的值是 p ⋅ 1 0 p o s − 1 p \cdot 10^{pos - 1} p10pos1,与 x 1 x_1 x1 拼接成: p x 1 px_1 px1,有: ( p x 1 ) 2 = ( p ⋅ 1 0 p o s − 1 + x 1 ) 2 = ( p ⋅ 1 0 p o s − 1 ) 2 + x 1 2 + 2 ⋅ x 1 ⋅ ( p ⋅ 1 0 p o s − 1 ) (px_1)^2 = (p \cdot 10^{pos-1} + x_1)^2 = (p \cdot 10^{pos-1})^2 + x_1 ^ 2 + 2 \cdot x1 \cdot (p \cdot 10^{pos - 1}) (px1)2=(p10pos1+x1)2=(p10pos1)2+x12+2x1(p10pos1)
其实这里就是一个完全平方公式。

那么我们转移就可以通过维护更低位的平方和 s 2 s_2 s2,符合条件的数有多少个 c n t cnt cnt,符合条件的数的 s 1 s_1 s1,三种信息。

转移就可以写成:
s 2 ′ = s 2 + 2 ⋅ s 1 ⋅ p ⋅ 1 0 p o s − 1 + c n t ⋅ ( p ⋅ 1 0 p o s − 1 ) 2 s_2\prime = s_2 + 2 \cdot s_1 \cdot p \cdot 10 ^{pos - 1} + cnt \cdot (p \cdot 10 ^ {pos -1})^2 s2=s2+2s1p10pos1+cnt(p10pos1)2
s 1 ′ = s 1 + c n t ⋅ p ⋅ 1 0 p o s − 1 s_1\prime = s_1 + cnt \cdot p \cdot 10 ^ {pos - 1} s1=s1+cntp10pos1
c n t ′ = c n t cnt\prime = cnt cnt=cnt

我们可以用 d p [ p o s ] [ r 1 ] [ r 2 ] dp[pos][r_1][r_2] dp[pos][r1][r2] 来表示 p o s pos pos 个全变化位, r 1 r_1 r1 为当前数位和模 7 7 7 的余数, r 2 r_2 r2 为当前数模 7 7 7 的余数条件下的信息。

搜到最底层时所有的位已经确定,所以节点只有一个信息 c n t = 1 cnt = 1 cnt=1 返回。
c n t cnt cnt 初始化为 − 1 -1 1 是为了记忆化,这里的 Z Z Z 类型当成是一个会自动取模的 l o n g l o n g long long longlong 就可以

时间复杂度: O ( l e n × 7 × 7 ) O(len \times 7 \times 7) O(len×7×7)

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;

const int INF=0x3f3f3f3e;
const long long INFLL=1e18;

typedef long long ll;

template<class T>
constexpr T power(T a, ll b){
    T res = 1;
    while(b){
        if(b&1) res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}

constexpr ll mul(ll a,ll b,ll mod){ //快速乘,避免两个long long相乘取模溢出
    ll res = a * b - ll(1.L * a * b / mod) * mod;
    res %= mod;
    if(res < 0) res += mod; //误差
    return res;
}

template<ll P>
struct MLL{
    ll x;
    constexpr MLL() = default;
    constexpr MLL(ll x) : x(norm(x % getMod())) {}

    static ll Mod;
    constexpr static ll getMod(){
       if(P > 0) return P;
       return Mod;
    }

    constexpr static void setMod(int _Mod){
       Mod = _Mod;
    }
    constexpr ll norm(ll x) const{
       if(x < 0){
           x += getMod();
       }
       if(x >= getMod()){
           x -= getMod();
       }
       return x;
    }
    constexpr ll val() const{
       return x;
    }
    explicit constexpr operator ll() const{ 
       return x; //将结构体显示转换为ll类型: ll res = static_cast<ll>(OBJ)
    }
    constexpr MLL operator -() const{ //负号,等价于加上Mod
       MLL res;
       res.x = norm(getMod() - x);
       return res;
    }
    constexpr MLL inv() const{
       assert(x != 0);
       return power(*this, getMod() - 2); //用费马小定理求逆
    }
    constexpr MLL& operator *= (MLL rhs) & { //& 表示“this”指针不能指向一个临时对象或const对象
       x = mul(x, rhs.x, getMod()); //该函数只能被一个左值调用
       return *this;
    }
    constexpr MLL& operator += (MLL rhs) & {
       x = norm(x + rhs.x);
       return *this;
    }
    constexpr MLL& operator -= (MLL rhs) & {
       x = norm(x - rhs.x);
       return *this;
    }
    constexpr MLL& operator /= (MLL rhs) & {
       return *this *= rhs.inv();
    }
    friend constexpr MLL operator * (MLL lhs, MLL rhs){
       MLL res = lhs;
       res *= rhs;
       return res;
    }
    friend constexpr MLL operator + (MLL lhs, MLL rhs){
       MLL res = lhs;
       res += rhs;
       return res;
    }
    friend constexpr MLL operator - (MLL lhs, MLL rhs){
       MLL res = lhs;
       res -= rhs;
       return res;
    }
    friend constexpr MLL operator / (MLL lhs, MLL rhs){
       MLL res = lhs;
       res /= rhs;
       return res;
    }
    friend constexpr std::istream& operator >> (std::istream& is, MLL& a){
       ll v;
       is >> v;
       a = MLL(v);
       return is;
    }
    friend constexpr std::ostream& operator << (std::ostream& os, MLL& a){
       return os << a.val();
    }
    friend constexpr bool operator == (MLL lhs, MLL rhs){
       return lhs.val() == rhs.val();
    }
    friend constexpr bool operator != (MLL lhs, MLL rhs){
       return lhs.val() != rhs.val();
    }
};

const ll mod = 1e9 + 7;
using Z = MLL<mod>;

struct node{
    Z cnt; //限制条件下与7无关的数字数量
    Z s1; //与7无关的数字的和
    Z s2; //与7无关的数字平方和

    node(ll cnt = -1, ll s1 = 0, ll s2 = 0): cnt(cnt), s1(s1), s2(s2) {}
    //cnt 初始化为 -1 是等价于memset的操作
};

node dp[20][8][8];
int num[20];
Z ten[20];

node dfs(int pos, int r1, int r2, bool limit){ //r1:当前数位和模7  r2:当前数模7
    if(!pos) return (r1 && r2 ? node(1) : node(0));
    if(!limit && dp[pos][r1][r2].cnt != -1) return dp[pos][r1][r2];
    node res(0);
    int up = (limit ? num[pos] : 9);
    fore(i, 0, up + 1){
        if(i == 7) continue;
        node nxt = dfs(pos - 1, (r1 + i) % 7, (r2 * 10 + i) % 7, limit && i == up);
        res.cnt += nxt.cnt;
        res.s1 += nxt.cnt * i * ten[pos - 1] + nxt.s1;
        res.s2 += nxt.s2 + nxt.cnt * i * i * ten[pos - 1] * ten[pos - 1];
        res.s2 += 2 * nxt.s1 * i * ten[pos - 1];
    }
    if(!limit) dp[pos][r1][r2] = res;
    return res;
}

Z solve(ll x){
    int len = 0;
    while(x){
        num[++len] = x % 10;
        x /= 10;
    }
    return dfs(len, 0, 0, true).s2;
}

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    ten[0] = 1;
    fore(i, 1, 19) ten[i] = ten[i - 1] * 10;
    int t;
    std::cin >> t;
    while(t--){
        ll l, r;
        std::cin >> l >> r;
        Z ans = solve(r) - solve(l - 1);
        std::cout << ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值