$30\%:n\le 200$ 直接枚举 $n-len+1$ 个区间,将这段里的数重新排序直接找到中位数
$60\%:n\le 2000$ 用主席树维护,查询区间第 $k$ 小。时间复杂度是 $\Theta(n^2\log ^2n)$,我只过了 $50$ 分。应该是用平衡树维护,时间复杂度是$\Theta(n^2\log n)
$100\%:n\le 10^5$ 前缀和 $+$ 前缀最小值 $+$ 二分答案。二分一个 $mid$,判断一下行不行,如何判断?在长度为 $n$ 的序列中,用一个b数组记录一下,$a[i]>mid\rightarrow b[i]=1,a[i]<=mid\rightarrow b[i]=-1.$ 然后记录b数组的前缀和sum,边记录边维护前缀最小值,如果现在扫到的下标已经可以和前面的构成一个合法的区间了。那么就可以判断如果 $sum[i]-sum_{min}>0$ 证明可以最为中位数,return true。
数位 $dp$,看上去好像有$10^{18}$ 种状态,但实际上只有九十多万。为什么呢?因为在 $0-9$ 中可以分为质数和合数($1$ 忽略):
- $4,6,8,9$- $2,3,5,7$
非质数可以看做是质数的乘积。那就可以看成由 $2357$ 这几个数字的乘积作为状态。而且 $2357$ 的最大个数可以找出来,就可以凑出好多状态。这些状态就最为 $dp$ 数组的第二维。
然后按照数位 $dp$ 的套路做记忆化。要注意前导 $0$处理,前导 $0$ 如果放到乘积里会导致整个乘积都变为 $0$,所以要将前导状态的 $0$ 看做是 $1$。
下面的代码只有90QAQ。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<queue> #define M 44800 #define ll long long using namespace std; ll read() { ll nm = 0, f = 1; char c = getchar(); for(; !isdigit(c); c = getchar()) if(c == '-') f = -1; for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0'; return nm * f; } ll cnt = 0; ll poww2[100], poww3[100], poww5[100], poww7[101]; ll dp[18][M]; ll ff[18][M]; int id[60][38][27][22]; ll l, r, ln, rn; const ll up = 16677181699666568ll; int num[22]; ll dfs(int len, bool f, bool qian, int a, int b, int c, int d, ll now, ll upd, ll down) { if(id[a][b][c][d] == 0) return 0; if(len <= 0 ) return (now >= down && now <= upd); if(!f && dp[len][id[a][b][c][d]] != -1 && !qian && now != 0) return dp[len][id[a][b][c][d]]; if(!f && ff[len][id[a][b][c][d]] != -1 && !qian && now == 0) return ff[len][id[a][b][c][d]]; ll ans = 0, top = (f ? num[len]:9); if(qian) { ans += dfs(len - 1, f &&(num[len] == 0), true, a, b, c, d, now, upd, down); } else ans += dfs(len - 1, f &&(num[len] == 0), false, a, b, c, d, 0, upd, down); ll op = now; if(now == 0 && qian) now = 1; for(int i = 1; i <= top; i++) { if(i == 1) ans += dfs(len - 1, f &&(num[len] == i), false, a, b, c, d, now * i, upd, down); if(i == 2) ans += dfs(len - 1, f &&(num[len] == i), false, a + 1, b, c, d, now * i, upd, down); if(i == 3) ans += dfs(len - 1, f &&(num[len] == i), false, a, b + 1, c, d, now * i, upd, down); if(i == 4) ans += dfs(len - 1, f &&(num[len] == i), false, a + 2, b, c, d, now * i, upd, down); if(i == 5) ans += dfs(len - 1, f &&(num[len] == i), false, a, b, c + 1, d, now * i, upd, down); if(i == 6) ans += dfs(len - 1, f &&(num[len] == i), false, a + 1, b + 1, c, d, now * i, upd, down); if(i == 7) ans += dfs(len - 1, f &&(num[len] == i), false, a, b, c, d + 1, now * i, upd, down); if(i == 8) ans += dfs(len - 1, f &&(num[len] == i), false, a + 3, b, c, d, now * i, upd, down); if(i == 9) ans += dfs(len - 1, f &&(num[len] == i), false, a, b + 2, c, d, now * i, upd, down); } if(!f && !qian && now != 0) dp[len][id[a][b][c][d]] = ans; if(!f && !qian && now == 0) ff[len][id[a][b][c][d]] = ans; return ans; } ll solve(ll x, ll y, ll z) { ll xn = x; int tp = 0; if(x < 0) return 0; while(x) { num[++tp] = x % 10; x /= 10; } memset(dp, -1ll, sizeof(dp)); ll as = dfs(tp, true, true, 0, 0, 0, 0, 0, y, z); // cout << xn << " " << y << " " << as << "\n"; return as; } int main() { l = read(), r = read(), ln = read(), rn = read(); poww2[0] = poww3[0] = poww5[0] = poww7[0] = 1; for(int i = 1; i <= 59; i++) { poww2[i] = poww2[i - 1] * 2; poww3[i] = poww3[i - 1] * 3; poww5[i] = poww5[i - 1] * 5; poww7[i] = poww7[i - 1] * 7; } for(int i = 0; i <= 59; i++) { ll now = poww2[i]; if(now > up) break; for(int j = 0; j <= 37; j++) { ll a = now * poww3[j]; if(a > up) break; for(int k = 0; k <= 26; k++) { ll b = a * poww5[k]; if(b > up) break; for(int l = 0; l <= 21; l++) { ll c = b * poww7[l]; if(c > up) break; id[i][j][k][l] = ++cnt; } } } } cout << solve(r, rn, ln) - solve(l - 1, rn, ln)<< "\n"; return 0; }
转载于:https://www.cnblogs.com/bljfy/p/9625476.html
