题意:首先定义了一种类数(标志数)
1:1到9都是标志数。
2:若x / 10是标志数,假设x /10在标志数中的排名是k, 若x的个位数小于k % 11, 那么x也是标志数。
现在给你一个字符串,问有多少个子串代表的数字是标志数?
思路:我们先假设已经求出的所有的标志数,并且知道每个标志数的rank(rank已经对11取模)。设dp[i][j]是第i个数字和第 i - 1位数字构成的2位数的rank为j,并且最高2位为这两位数字的合法数字的数目。可能有点拗口,我们举一个例子。
假设字符串是321, 我们计算合法的子串有多少个。首先,dp[1][1]是1,所以1对答案的贡献是1。我们再看2对答案的贡献,合法的串有21, 2。2对答案的贡献是1,21是怎么来的呢?21的rank是1,所以dp[2][2] += dp[3][1],21来源于dp[3][1]。我们在看3对答案的贡献,3构成的合法的串是3, 32, 321。3对答案的贡献是1,那么32, 321是怎么来的呢?32的rank是6, 所以需要知道dp[2][6]是多少。2位置本身有个2,可以构成32,那么321怎么来的?321的rank是4,所以需要知道dp[3][4]是多少,dp[3][4]是1,所以返回1就行了。那么我们直接提前把答案的贡献累加上,当2前面的数与2构成的数rank是6的时候,可以知道此时前面的数与21构成的数(rank是4)也是对答案有贡献的,所以dp[2][6]是2。转移的时候可以暴力打表,打出每个2位数的rank,或者算出来就行了。
具体做法看我队友的博客就行了。
代码:
#include<bits/stdc++.h> #define LL long long using namespace std; const int maxn = 100010; LL dp[maxn][11]; LL ans = 0; char s[maxn]; int main() { scanf("%s",s + 1); int n = strlen(s + 1); for (int i = n; i >= 1; i--) { for (int j = 0; j <= 10; j++) { dp[i][j] = 1; int t = s[i + 1] - '0'; if(i < n && j > t) { dp[i][j] += dp[i + 1][(j * (j - 1) / 2 + t + 10) % 11]; } } if(s[i] != '0') ans += dp[i][s[i] - '0']; } printf("%lld ", ans); }