B - 排列
next_permutation
inline void read(int &x){
int data = 0, w = 1;
char ch = getchar();
while(ch != '-' && !isdigit(ch))
ch = getchar();
if(ch == '-')
w = -1, ch = getchar();
while(isdigit(ch))
data = 10 * data + ch - '0', ch = getchar();
x = data * w;
}
void write(int x){
if(x < 0)
putchar('-'), x = -x;
if(x > 9)
write(x / 10);
putchar('0' + x % 10);
}
const int maxn = 1024 + 5;
int n, m, a[maxn];
int main()
{
int t; read(t);
while (t--) {
read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
while (m) { next_permutation(a + 1, a + n + 1); m--; }
for (int i = 1; i <= n; ++i) {
write(a[i]);
putchar(i == n ? '\n' : ' ');
}
}
}
C - Round Numbers
求二进制区间内0的数量大于等于1的数量的十进制数的个数
数位dp,用一个变量保存0和1的数量差,因为中途可能出现差小于0的情况,我们加上32使其大于等于0即可
const int maxn = 32 + 5;
int dp[maxn][maxn << 1], a[maxn];
int f(int pos, int num, bool limit, bool lead) {
if (pos == -1) return num >= 0;
if (!limit && !lead && dp[pos][num + maxn] != -1) return dp[pos][num + maxn];
int up = limit ? a[pos] : 1, sum = 0;
for (int i = 0; i <= up; ++i) {
if (lead && i == 0) sum += f(pos - 1, num, limit && i == a[pos], lead && i == 0);
else sum += f(pos - 1, num + (i == 0 ? 1 : -1), limit && i == a[pos], lead && i == 0);
}
if (!limit && !lead) dp[pos][num + maxn] = sum;
return sum;
}
int solve(int x) {
int p = 0;
while (x) {
a[p++] = x % 2;
x /= 2;
}
return f(p - 1, 0, true, true);
}
int main()
{
memset(dp, -1, sizeof(dp));
int a, b; read(a), read(b);
write(solve(b) - solve(a - 1));
putchar('\n');
}
D - 好题
求升序并小于给出的字符串的数量
数位dp,用变量保存当前选的字符
char str[12];
ll dp[12][30];
ll f(int pos, int ch, bool limit, bool lead) {
//printf("pos = %d, ch = %d, limit = %d\n", pos, ch, limit);
if (pos == -1) return ch > 0;
if (!limit && dp[pos][ch] != -1) return dp[pos][ch];
int up = limit ? str[pos] - 'a' + 1 : 26; ll sum = 0;
//printf("%d-%d\n", limit, up);
for (int i = lead ? 0 : ch + 1; i <= up; ++i) {
sum += f(pos - 1, i, limit && i == up, lead && i == 0);
}
if (!limit) dp[pos][ch] = sum;
return sum;
}
int main()
{
memset(dp, -1, sizeof(dp));
while (~scanf("%s", &str)) {
int n = strlen(str);
bool flg = true;
for (int i = 1; i < n; ++i) {
if (str[i] <= str[i - 1]) {
flg = false;
break;
}
}
if (!flg) { printf("0\n"); continue; }
reverse(str, str + n);
printf("%lld\n", f(n - 1, 0, true, true));
}
}
E - Number Sequence
先找规律,发现 1 ∼ 9 1\sim 9 1∼9之间每次长度都加1, 10 ∼ 99 10\sim 99 10∼99之间每次长度都加2, 100 ∼ 999 100\sim 999 100∼999之间每次都加3,依次类推;数的范围从9到90到900…,公差从1到2到3…。
发现给出范围内公差最多到5,我们暴力找到每个数在的公差的大范围
再二分找到具体的数字
int main()
{
int t; scanf("%d", &t);
while (t--) {
ll k; scanf("%lld", &k);
int d = 0, a1 = 0, sz = 9;
ll sum = 0;
while (true) {
d++; a1 += d;
ll tmp = 1ll * a1 * sz + 1ll * sz * (sz - 1) / 2 * d;
if (sum + tmp < k) {
sum += tmp;
a1 = a1 + (sz - 1) * d; sz *= 10;
continue;
}
int l = 0, r = 1e6;
while (l != r) {
int n = (l + r) >> 1;
if (1ll * a1 * n + 1ll * n * (n - 1) / 2 * d >= k - sum) r = n;
else l = n + 1;
}
ll pre = 1ll * a1 * (l - 1) + 1ll * (l - 1) * (l - 2) / 2 * d;
sum += pre;
k = k - sum;
int cnt = 0;
for (int i = 1; ; ++i) {
if (1 <= i && i <= 9) cnt++;
else if (10 <= i && i <= 99) cnt += 2;
else if (100 <= i && i <= 999) cnt += 3;
else if (1000 <= i && i <= 9999) cnt += 4;
else if (10000 <= i && i <= 99999) cnt += 5;
if (cnt >= k) {
cnt -= k;
while (i) {
if (cnt == 0) {
write(i % 10); putchar('\n');
break;
}
cnt--;
i /= 10;
}
break;
}
}
break;
}
}
}
F - Paths on a Grid
经典排列组合,方案数为 ( n + m m ) {n+m\choose m} (mn+m)
int main()
{
ll n, m;
while (~scanf("%lld%lld", &n, &m)) {
if (n == 0 && m == 0) break;
if (n > m) swap(n, m);
double den = 1, mol = 1;
for (ll i = m + 1; i <= n + m; ++i) {
den *= 1.0 * i;
mol *= 1.0 * (i - m);
}
printf("%.0f\n", den / mol);
}
}
G - Word Index
D - 好题 同一道题
char str[12];
ll dp[12][30];
ll f(int pos, int ch, bool limit, bool lead) {
//printf("pos = %d, ch = %d, limit = %d\n", pos, ch, limit);
if (pos == -1) return ch > 0;
if (!limit && dp[pos][ch] != -1) return dp[pos][ch];
int up = limit ? str[pos] - 'a' + 1 : 26; ll sum = 0;
//printf("%d-%d\n", limit, up);
for (int i = lead ? 0 : ch + 1; i <= up; ++i) {
sum += f(pos - 1, i, limit && i == up, lead && i == 0);
}
if (!limit) dp[pos][ch] = sum;
return sum;
}
int main()
{
memset(dp, -1, sizeof(dp));
while (~scanf("%s", &str)) {
int n = strlen(str);
bool flg = true;
for (int i = 1; i < n; ++i) {
if (str[i] <= str[i - 1]) {
flg = false;
break;
}
}
if (!flg) { printf("0\n"); continue; }
reverse(str, str + n);
printf("%lld\n", f(n - 1, 0, true, true));
}
}
H - The Last Non-zero Digit
求阶乘的最后一位非0数
参考数学相关
int get_prime_factor(int n, int x) {
if (!n) return 0;
return n / x + get_prime_factor(n / x, x);
}
int g(int n, int x) {
if (!n) return 0;
return n / 10 + (n % 10 >= x) + g(n / 5, x);
}
int f(int n, int x) {
if (!n) return 0;
return f(n / 2, x) + g(n, x);
}
//2,3,7,9的循环节,其中注意若2的个数为0的话循环节第一位应该为1
int cyclic_section[][4] = {{6, 2, 4, 8}, {1, 3, 9, 7}, {1, 7, 9, 3}, {1, 9, 1, 9}};
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
int cnt2 = get_prime_factor(n, 2) - get_prime_factor(n - m, 2);
int cnt5 = get_prime_factor(n, 5) - get_prime_factor(n - m, 5);
int cnt3 = f(n, 3) - f(n - m, 3);
int cnt7 = f(n, 7) - f(n - m, 7);
int cnt9 = f(n, 9) - f(n - m, 9);
int ans = 1;
if (cnt5 > cnt2) {
printf("5\n"); continue;
}
if (cnt2 != cnt5) {
ans *= cyclic_section[0][(cnt2 - cnt5) % 4];
ans %= 10;
}
ans *= cyclic_section[1][cnt3 % 4]; ans %= 10;
ans *= cyclic_section[2][cnt7 % 4]; ans %= 10;
ans *= cyclic_section[3][cnt9 % 4]; ans %= 10;
printf("%d\n", ans);
}
}
I - Hexadecimal Numbers
不能选已经选的字母,其他 n n n字母选出 m m m进行排列的方案数为 A n m A_n^m Anm
每个位置从能选的字母从大到小判断,没有则选0
int vis[20], ans[20];
ll c[20][10], fib[10];
int main()
{
for (int i = 1; i <= 16; ++i) {
c[i][0] = c[i][i] = 1;
for (int j = 1; j < i; ++j) {
c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
}
}
fib[0] = 1; for (int i = 1; i <= 7; ++i) fib[i] = fib[i - 1] * i;
ll k;
while (~scanf("%lld", &k)) {
int cnt = 0;
bool lead = true;
for (int i = 8, j; i > 0; --i) {
for (j = 15; j >= 1; --j) {
if (vis[j]) continue;
ll tmp = c[16 - cnt - 1][i - 1] * fib[i - 1];
if (tmp < k) k -= tmp;
else { vis[j] = true; break; }
}
if (!lead || j != 0) cnt++;
if (j != 0) lead = false;
if (j == 0 && lead) continue;
printf("%c", j >= 10 ? j - 10 + 'A' : j + '0');
}
putchar('\n');
}
}
J - The Counting Problem
计算范围内每个数出现的个数
我们枚举每个位置,该位置把数分成三个部分,左边的数为 l e f t left left,该数字 n u m num num,右边的数为 r i g h t right right,枚举当前位置的数 i i i
c n t = { { l e f t × 1 0 l e n + r i g h t + 1 , i = = n u m l e f t × 1 0 l e n + 1 0 l e n , n u m > i , i ≠ 0 { ( l e f t − 1 ) × 1 0 l e n + r i g h t + 1 , n u m = = i ( l e f t − 1 ) × 1 0 l e n + 1 0 l e n , n u m > i , i = 0 cnt=\begin{cases}\begin{cases}left\times{10^{len}}+right+1,i==num\\left\times10^{len}+10^{len},num>i \end{cases},i\ne0\\\begin{cases}(left-1)\times10^{len}+right+1,num==i\\(left-1)\times10^{len}+10^{len},num>i \end{cases},i=0\end{cases} cnt=⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧{left×10len+right+1,i==numleft×10len+10len,num>i,i=0{(left−1)×10len+right+1,num==i(left−1)×10len+10len,num>i,i=0
int ans[10];
ll fib[10];
int solve(int x, int y) {
int res = 0;
for (int i = 1; i <= 9; ++i) {
int l = x / fib[i - 1];
int r = x - l * fib[i - 1];
int rt = l % 10; l /= 10;
//printf("%d - %d - %d\n", l, rt, r);
if (y == 0) {
if (l) {
res += (l - 1) * fib[i - 1];
if (rt == 0) res += r + 1;
else if (rt > y) res += fib[i - 1];
}
}
else {
res += l * fib[i - 1];
if (rt > y) res += fib[i - 1];
else if (rt == y) res += r + 1;
}
if (l <= 0) break;
}
return res;
}
int main()
{
fib[0] = 1; for (int i = 1; i <= 9; ++i) fib[i] = fib[i - 1] * 10;
int a, b;
while (~scanf("%d%d", &a, &b)) {
if (a == 0 && b == 0) break;
if (a > b) swap(a, b);
memset(ans, 0, sizeof(ans));
for (int i = 0; i <= 9; ++i) {
printf("%d%c", solve(b, i) - solve(a - 1, i), i == 9 ? '\n' : ' ');
}
}
}
K - How many 0’s?
J的简化版
ll fib[12];
ll solve(ll x) {
if (x < 0) return -1;
ll res = 0;
for (int i = 1; i <= 12; ++i) {
ll l = x / fib[i - 1];
ll r = x - l * fib[i - 1];
int rt = l % 10; l /= 10;
if (!l) break;
res += (l - 1) * fib[i - 1];
if (rt) res += fib[i - 1];
else res += r + 1;
}
return res;
}
int main()
{
fib[0] = 1; for (int i = 1; i <= 12; ++i) fib[i] = fib[i - 1] * 10;
ll a, b;
while (~scanf("%lld%lld", &a, &b)) {
if (a == -1 && b == -1) break;
if (a == 0 && b == 0) printf("1\n");
else printf("%lld\n", solve(b) - solve(a - 1));
}
}
L - Binary Stirling Numbers
给出 S ( 0 , 0 ) = 1 S(0,0)=1 S(0,0)=1,其他全为0,因此答案要为1,就要考虑使 n = 0 , m = 0 n=0,m=0 n=0,m=0的方案数
S ( n , m ) = m S ( n − 1 , m ) + S ( n − 1 , m − 1 ) S(n,m)=mS(n-1,m)+S(n-1,m-1) S(n,m)=mS(n−1,m)+S(n−1,m−1)分奇偶讨论
在 n × m n\times m n×m的矩阵里,设 S ( n − 1 , m ) S(n-1,m) S(n−1,m)为操作 1 1 1, S ( n − 2 , m − 2 ) S(n-2,m-2) S(n−2,m−2)为操作 2 2 2,因此必须有 m + 1 2 \frac{m+1}{2} 2m+1的操作 2 2 2, n − m n-m n−m个操作 1 1 1,对 m + 1 2 \frac{m+1}{2} 2m+1进行插空 n − m n-m n−m得到 ( m + 1 2 + n − m m + 1 2 ) {\frac{m+1}{2}+n-m\choose \frac{m+1}{2}} (2m+12m+1+n−m)
对 ( n m ) {n\choose m} (mn)判断奇偶性,处理 n , n − m , m n,n-m,m n,n−m,m的2的因子数,如果因子数相减正好为0即为奇数
int get(int x) {
int ret = 0;
for (int i = 2; i <= x; i <<= 1) {
ret += x / i;
}
return ret;
}
bool solve(int n, int m) {
return get(n) - get(m) - get(n - m) == 0;
}
int main()
{
int t; read(t);
while (t--) {
int n, m; read(n); read(m);
int decre = n - m, k = (m + 1) / 2;
println(solve(decre + k - 1, k - 1));
}
}
M - Birthday Cake
差分和牛顿公式
参考数学相关
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.Scanner;
public class Main{
Scanner scan = new Scanner(System.in);
BigInteger c[]=new BigInteger[110];
BigInteger h[][] = new BigInteger[110][110];
void getc(BigInteger n, int m) {
c[1] = n;
for (int i = 2; i <= m+1; i++)
c[i] = c[i - 1].multiply(n.subtract(BigInteger.valueOf(i - 1)))
.divide(BigInteger.valueOf(i));
}
PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));
void run() {
int cas=scan.nextInt();
while(cas-->0){
BigInteger n =scan.nextBigInteger().add(BigInteger.ONE);
int m = scan.nextInt();
for (int i = 0; i <= m; i++)
h[0][i] = BigInteger.valueOf(i).pow(m);
for (int i = 1; i <= m; i++)
for (int j = 0; j <= m - i; j++)
h[i][j] = h[i - 1][j + 1].subtract(h[i - 1][j]);
BigInteger ans = BigInteger.ZERO;
getc(n, m);
for (int i = 0; i <= m; i++)
ans=ans.add(h[i][0].multiply(c[i+1]));
out.println(ans);
out.flush();
}
}
public static void main(String[] args) {
new Main().run();
}
}
N - Sum of powers
伯努利数解决等幂求和
参考数学相关
typedef long long ll;
const int maxn = 20 + 3;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
ll ret = a / gcd(a, b) * b;
return ret ? ret : -ret;
}
struct fraction {
ll a, b;
fraction() {}
fraction(ll x) { a = x; b = 1; }
fraction(ll x, ll y) { a = x; b = y; }
void deal() {
if (b < 0) b = -b, a = -a;
ll k = gcd(a, b);
if (k < 0) k = -k;
a /= k; b /= k;
}
fraction operator +(const fraction& rhs) const {
fraction ans;
ans.b = lcm(b, rhs.b);
ans.a = ans.b / b * a + ans.b / rhs.b * rhs.a;
ans.deal();
return ans;
}
fraction operator -(const fraction& rhs) const {
fraction ans;
ans.b = lcm(b, rhs.b);
ans.a = ans.b / b * a - ans.b / rhs.b * rhs.a;
ans.deal();
return ans;
}
fraction operator *(const fraction& rhs) const {
fraction ans;
ans.a = a * rhs.a;
ans.b = b * rhs.b;
ans.deal();
return ans;
}
fraction operator /(const fraction& rhs) const {
fraction ans;
ans.a = a * rhs.b;
ans.b = b * rhs.a;
ans.deal();
return ans;
}
void println() {
printf("%lld/%lld\n", a, b);
}
};
fraction B[maxn];
ll C[maxn][maxn];
void init() {
for (int i = 1; i < maxn; ++i) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; ++j) {
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}
}
B[0] = fraction(1);
for (int i = 1; i <= 20; ++i) {
B[i] = fraction(0);
for (int j = 0; j < i; ++j)
B[i] = B[i] - fraction(C[i + 1][j]) * B[j];
B[i] = B[i] / fraction(C[i + 1][i]);
}
}
int n; fraction a[maxn];
int main() {
init();
while (~scanf("%d", &n)) {
ll Lcm = 1;
for (int i = 0; i <= n; ++i) {
a[i] = fraction(C[n + 1][i]) * B[i] * fraction(1, n + 1);
Lcm = lcm(Lcm, a[i].b);
}
printf("%lld ", Lcm);
a[1] = a[1] + fraction(1);
for (int i = 0; i <= n; ++i)
printf("%lld ", Lcm / a[i].b * a[i].a);
puts("0");
}
}
O - Ignatius and the Princess III
dfs
const int maxn = 120 + 5;
int dp[maxn][maxn];
int solve(int n, int m) {
if (n == 0) return 1;
if (m > n) return 0;
if (dp[n][m] != -1) return dp[n][m];
int res = 0;
for (int i = m; i <= n; ++i) {
if (n - i >= i) {
res += solve(n - i, i);
}
else if (i == n)
res += solve(0, i);
}
return dp[n][m] = res;
}
int main()
{
memset(dp, -1, sizeof(dp));
int n;
while (~scanf("%d", &n)) {
println(solve(n, 1));
}
}
P - Train Problem II
大数+卡塔兰数经典应用
const int maxn = 100 + 5;
struct Bigint
{
vector s;
Bigint() { s.clear(); }
Bigint operator =(int n) {
while (n) {
s.push_back(n % 10);
n /= 10;
}
return *this;
}
friend Bigint operator *(const Bigint& a, const int& b) {
Bigint res; int k = 0;
for (int i = 0; i < a.s.size(); ++i) {
res.s.push_back((k + a.s[i] * b) % 10);
k = (k + a.s[i] * b) / 10;
}
while (k) {
res.s.push_back(k % 10);
k /= 10;
}
return res;
}
friend Bigint operator /(const Bigint& a, const int& b) {
Bigint res; int k = 0;
for (int i = a.s.size() - 1; i >= 0; --i) {
res.s.push_back((k * 10 + a.s[i]) / b);
k = (k * 10 + a.s[i]) % b;
}
reverse(res.s.begin(), res.s.end());
while (res.s.back() == 0) res.s.pop_back();
return res;
}
void print() {
for (int i = s.size() - 1; i >= 0; --i) {
putchar(s[i] + '0');
}
}
};
Bigint C[maxn];
int main()
{
C[0] = 1;
for (int i = 1; i <= 100; ++i) {
C[i] = C[i - 1] * (4 * i - 2);
C[i] = C[i] / (i + 1);
}
int n; while (~scanf("%d", &n)) {
C[n].print(); putchar('\n');
}
}
Q - 排列组合
指数型母函数解析
double fib(int n) {
double res = 1.0;
for (int i = 1; i <= n; ++i) {
res = res * i;
}
return res;
}
double a[maxn], f[maxn], g[maxn];
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
for (int i = 0; i < n; ++i) {
scanf("%lf", &a[i]);
}
for (int i = 0; i <= a[0]; ++i) {
f[i] = 1.0 / fib(i);
}
for (int i = 1; i < n; ++i) {
for (int j = 0; j <= m; ++j) {
for (int k = 0; k <= a[i] && j + k <= m; ++k) {
g[j + k] += f[j] / fib(k);
}
}
for (int j = 0; j <= m; ++j) {
f[j] = g[j]; g[j] = 0;
}
}
printf("%.0lf\n", f[m] * fib(m));
}
}
S - Factstone Benchmark
斯特灵公式 n ! ∼ 2 π n ( n e ) n n!\sim\frac{\sqrt{2\pi n}}{(\frac{n}{e})^n} n!∼(en)n2πn
即为 n ! < 2 2 i n!<2^{2^i} n!<22i,两边求对数 l o g 2 log_2 log2得 1 2 l o g 2 ( 2 π n ) ⋅ n l o g 2 ( n e ) < 2 i , i ≤ 24 \frac{1}{2}log_2(2\pi n)\cdot nlog_2(\frac{n}{e})<2^i,i\le24 21log2(2πn)⋅nlog2(en)<2i,i≤24,暴力求最大满足n
typedef long long ll;
//n! = \sqrt{2\pi n}\frac{n}{e}^n
//log_2{n!} = \frac{1}{2}log_2{2\pi n}nlog_2{\frac{n}{e}}
int a[]={3,5,8,12,20,34,57,98,170,300,536,966,1754,3210,5910,10944,20366,38064,71421,134480,254016};
int main(){
int n;
while(scanf("%d",&n),n){
printf("%d\n",a[(n-1960)/10]);
}
return 0;
}
T - 找单词
普通母函数解析
int a[maxn];
ll f[maxn], g[maxn];
int main()
{
int t; scanf("%d", &t);
while (t--) {
for (int i = 1; i <= 26; ++i) scanf("%d", &a[i]);
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
for (int i = 0; i <= a[1]; ++i) {
f[i] = 1;
}
for (int i = 2; i <= 26; ++i) {
for (int j = 0; j <= 50; ++j) {
for (int k = 0; k <= a[i] && i * k + j <= 50; ++k) {
g[i * k + j] += f[j];
}
}
for (int j = 0; j <= 50; ++j) {
f[j] = g[j]; g[j] = 0;
}
}
ll ans = 0;
for (int i = 1; i <= 50; ++i) ans += f[i];
printf("%lld\n", ans);
}
}