[hihocoder1259] A Math Problem (数学,数位dp)

题目链接:http://hihocoder.com/problemset/problem/1259

式子可以化简,并且找到一个f的递推式,递推过程如下:

列出来后手动枚举几个f的值,会发现函数值其实就是把二进制的自变量当成三进制来算一次。

答案求的是异或和,要求将所有余数相同的和异或起来,可以考虑用数位dp了,f(i,j)表示长为i的时候,余数为j。由于从高到低更新,那么f(i,j)=∑f(i-1, j+3^(i-1)*(k==1)),k取值为0或者1。倒着推可以一直用期望的余数减去当前加上某位值后的余数,判断最后到头的时候余数是不是0。

这么交是会TLE的,原因是每次都要clear dp数组。

由于只有5个模数,不放再多加一维,记录各个模下的不同余数个数和。

再预处理一下不同模下的幂取模值就行了。

特别注意下:f(0)不存在,但是dp出来f(0)=0,%k=0,所以在处理的时候要减掉这个值。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 const int maxn = 66;
 6 const int maxm = 66666;
 7 const int mod[5] = {3,5,17,257,65537};
 8 LL n;
 9 int k, id;
10 LL dp[6][maxn][maxm];
11 int mul[6][maxn];
12 int digit[maxn];
13 
14 LL dfs(int l, int sum, bool flag) {
15     if(l == 0) return sum == 0;
16     if(!flag && ~dp[id][l][sum]) return dp[id][l][sum];
17     int pos = flag ? digit[l] : 1;
18     LL ret = 0;
19     for(int i = 0; i <= pos; i++) {
20         if(i == 0) ret += dfs(l-1, sum, flag&&(i==pos));
21         else ret += dfs(l-1, (sum+k-mul[id][l-1])%k, flag&&(i==pos));
22     }
23     if(!flag) dp[id][l][sum] = ret;
24     return ret;
25 }
26 
27 LL f(LL n, int sum) {
28     int pos = 0;
29     while(n) {
30         digit[++pos] = n % 2;
31         n >>= 1;
32     }
33     return dfs(pos, sum, true);
34 }
35 
36 int main() {
37     // freopen("in", "r", stdin);
38     int T;
39     scanf("%d", &T);
40     for(int i = 0; i < 5; i++) {
41         mul[i][0] = 1;
42         for(int j = 1; j < maxn; j++) {
43             mul[i][j] = (mul[i][j-1] * 3) % mod[i];
44         }
45     }
46     memset(dp, -1, sizeof(dp));
47     while(~scanf("%lld%d",&n,&k)) {
48         id = lower_bound(mod, mod+5, k) - mod;
49         LL ret = 0;
50         for(int i = 0; i < k; i++) ret ^= (f(n, i) - (i == 0));
51         printf("%lld
", ret);
52     }
53     return 0;
54 }
原文地址:https://www.cnblogs.com/kirai/p/6824812.html