Codeforces-121C(逆康托展开)
题目大意:
给你两个数n,k求n的全排列的第k小,有多少满足如下条件的数:
首先定义一个幸运数字:只由4和7构成
对于排列p[i]满足i和p[i]都是幸运数字
思路:
对于n,k<=1e9
一眼逆康托展开
什么?你不知到康托展开?
由于阶乘的增长是非常快的
13的阶乘就大于1e9了
所以说:
对于一个n的权排列 1 2 3 4 ...... n
我们最多动他的后13位就可以得到第k小的排列
我们称之为动n的后x位可以得到第k小的排列(如果这里都取13的话,有的序列是n<13的,会越界)
然后我们对[1,n-x]中的数字统计答案的时候可以数位dp,可以dfs
然后对后x位统计答案,就是裸的逆康托展开了
Code:
ll n, k, x, fac[20], ans; std::vector<ll> v; ll suf[20], cnt; void dfs(ll num, ll top) { // cout<<top<<endl; if(num > top) return ; if(num <= top && num != 0)ans++; dfs(num * 10 + 4, top); dfs(num * 10 + 7, top); } int ok(ll x) { int flag = 1; while(x) { int yy = x % 10; // cout<<yy<<"@"<<endl; if(yy != 4 && yy != 7) flag = 0; x /= 10; } return flag; } void re_count() { sort(v.begin(), v.end()); for(int i = x ; i >= 1 ; i--) { ll pos = k / fac[i - 1]; k = k % fac[i - 1]; suf[++cnt] = v[pos]; v.erase(v.begin() + pos); } } int main() { fac[0] = 1; rep(i, 1, 16) fac[i] = i * fac[i - 1]; n = read(), k = read(); k--; for(int i = 1 ; i <= 16; i++) { if(fac[i] >k) { x = i; break; } } // cout<<x<<"#"<<endl; for(int i = n; i >= n - x + 1; i--) v.push_back(i); if(n - x < 0) { cout << -1; return 0; } dfs(0, n - x); //搜出n-x的幸运数 re_count(); for(int i = n - x + 1; i <= n; i++) if(ok(i) && ok(suf[i - (n - x)])) ans++; out(ans); return 0; }

浙公网安备 33010602011771号