P7976 解题报告

题目传送门

题目大意:

设函数 F(x):=(x+1)mod31T 次询问,计算:

i=0njF((ij))

思路:

看到奇奇怪怪的组合数求和首先考虑 Lucas,将原数在 3 进制下拆位,得:

(ij)=k=1m(ikjk)mod3

其中 m 表示 i 和三进制下较长的那个数的数字位数。

接着注意到 F 函数是一个积性函数(这个可以分九类讨论证明),即 F(xy)=F(x)F(y),所以实际上 F((ij)) 要计算的就是所有 F((ikjk)) 的乘积。

对于每一个 ij 的每一位就独立了,这时候再分类讨论:

  1. ik=0 时,jk0 的时候有贡献,此时这一位的值为 F(1)=1
  2. ik=1 时,jk0,1 的时候有贡献,此时这一位的值为 F(1)+F(1)=2
  3. ik=2 时,jk0,1,2 的时候有贡献,此时这一位的值为 F(1)+F(2)+F(1)=1

而乘 1 是不会是答案增加的,所以只用考虑乘 2 的个数就行了,即:

jF((ij))=(ik=01)(ik=12)(ik=21)=2#{ik=1}

然后就会发现统计一下三进制表示下 1 的个数就行了,数位 dp 即可。

Code:

#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;

const int N = 65, mod = 1732073999;

int T;
ll vmax, n;
vector<int> num;
ll f[N][N];

ll qpow(int a, int b) {
    ll ans = 1, base = a;
    while(b) {
        if(b & 1) ans = ans * base % mod;
        base = base * base % mod;
        b >>= 1;
    }
    return ans;
}

ll dfs(int pos, int cnt, bool limit, bool zero) {
    if(pos < 0) return qpow(2, cnt);
    if(!limit && !zero && ~f[pos][cnt]) return f[pos][cnt];
    int mx = (limit ? num[pos] : 2);
    ll res = 0;
    for(int i = 0; i <= mx; i++)
        res = (res + dfs(pos - 1, cnt + (i == 1), limit && (i == num[pos]), zero && (!i))) % mod;
    if(!limit && !zero) f[pos][cnt] = res;
    return res;
}

ll calc(ll x) {
    num.clear();
    ll tmp = x;
    while(tmp) {
        num.push_back(tmp % 3);
        tmp /= 3;
    }
    return dfs(num.size() - 1, 0, 1, 1);
}

int main() {
    scanf("%d%lld", &T, &vmax);
    memset(f, -1, sizeof f);
    while(T--) {
        scanf("%lld", &n);
        printf("%lld\n", calc(n));
    }
    return 0;
}
posted @   Brilliant11001  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示