「POJ3531」Alternating Sum of Digits
这个被标算踩爆的做法本质上是一个大模拟
考虑随便选一个数,考虑它对整体的贡献。很容易发现,这个数的贡献取决于比它小的数对数字串贡献的数字总位数的奇偶性,因此考虑用数位 \(dp\) 去计算所有数字的贡献。
考虑动态对数位填数(没有填的部分视作0),动态计算已经比当前数小的数对数位填的数对串的位数贡献的奇偶性。很明显新填上一位数带来的贡献与该位置离最高位的距离有关系,假设当前填上的数是 \(i\) ,距离最高位的距离为 \(h\) ,当前数位是 \(x\) ,则很容易得到新的贡献为:
\[i \times k^{x-1} \times (h + x)
\]
其中 \(i \times k^{x-1}\) 表示填上后新增的数字个数,而每个数字贡献位数为 \((h + x)\) 。
考虑一个边界情况,我们添加的数恰好是最高位 \(x\) ,填的数是 \(i\) 。
实际上对于这种情况我们可以预处理一个 \(dp\) 。 我们不妨定义 \(val_{x,i}\) 表示将 \(i\) 数字填到最高位 \(x\) 上 , 其它位视作0 ,比它小的数的数位贡献。
于是很容易有如下转移 \(val_{x , i} = val_{x , i - 1} + k^{x-1} \times x\)。
同时也有一个边界情况很容易可以得到 \(val_{x , 1} = val_{x - 1 , 1} + k^{x - 2} \times (x - 1) \times (k - 1)\)。
以上算贡献的部分可以对2取膜,存进 \(dp\) 里面就好了。
时间复杂度就是 \(O(len^3 \times k)\) ,其中 \(len\) 为数位长度,\(k\) 为进制。
细节略多。
另外数据造大了,可能需要开 __int128。
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define pii pair <int , int>
#define mp make_pair
#define fs first
#define sc second
using namespace std;
typedef __int128 LL;
template <typename T>
void read(T &x) {
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s-'0');s=getchar();}
x *= f;
}
template <typename T>
void write(T x , char s='\n') {
if(!x) {putchar('0');putchar(s);return;}
if(x<0) {putchar('-');x=-x;}
T tmp[25] = {} , t = 0;
while(x) tmp[t ++] = x % 10 , x /= 10;
while(t -- > 0) putchar(tmp[t] + '0');
putchar(s);
}
LL dp[2][2][2][70][70][181] , k , len , val[70][15] , tmp[70] , b[70];
char s[70];
// lim lead0 num highest pos sum
void pre() {
val[1][1] = 0;
for (int i = 2; i < k; ++i) val[1][i] = (val[1][i - 1] + 1) & 1;
b[0] = tmp[1] = 1;
b[1] = k;
for (int i = 2; i <= len; ++i) {
val[i][1] = (val[i - 1][1] + tmp[i - 1] * (k - 1)) & 1;
tmp[i] = (b[i - 1] * i) & 1;
b[i] = (b[i - 1] * k) & 1;
for (int j = 2; j < k; ++j) val[i][j] = (val[i][j - 1] + tmp[i]) & 1;
}
}
LL dfs(int lim , int lead , int num , int h , int x , int Sum) {
if(dp[lim][lead][num][h][x][Sum] != -1) return dp[lim][lead][num][h][x][Sum];
if(!x) {
if(lead) return dp[lim][lead][num][h][x][Sum] = 0;
else if(!num) return dp[lim][lead][num][h][x][Sum] = Sum - 90;
return dp[lim][lead][num][h][x][Sum] = -(Sum - 90);
}
int md = lim?s[x]-'0':k-1;
dp[lim][lead][num][h][x][Sum] = 0;
for (int i = 0; i <= md; ++i) {
int nl = (i == md && lim)?1:0;
if(i == 0 && lead) dp[lim][lead][num][h][x][Sum] += dfs(nl , 1 , 0 , 0 , x - 1 , Sum);
else dp[lim][lead][num][h][x][Sum] += dfs(nl , 0 , lead?val[x][i]:(num + b[x - 1] * i * (x + h) % 2) % 2 , lead?1:h+1 , x - 1 , (lead?1:(h+1)&1)==1?Sum + i:Sum - i);
}
return dp[lim][lead][num][h][x][Sum];
}
int main() {
read(k) , scanf("%s" , s + 1);
len = strlen(s + 1);
reverse(s + 1 , s + 1 + len);
pre();
for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 1; ++j) for (int k = 0; k <= 1; ++k) for (int w = 0; w <= len; ++w) for (int p = 0; p <= len; ++p) for (int q = 0; q <= 180; ++q) dp[i][j][k][w][p][q] = -1;
write(dfs(1 , 1 , 0 , 0 , len , 90));
return 0;
}