【LOJ3074】「2019 集训队互测 Day 3」操作序列计数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/89788875

【题目链接】

【思路要点】

  • 考虑计算 2 2 号操作不超过 i i 次的方案数。
  • 它应当等于 2 2 k 0 k^0 的倍数、 1 1 k 1 k^1 的倍数、 1 1 k 2 k^2 的倍数、……、 1 1 k i k^i 的倍数总和为 N N 的方案数,从高位向低位 d p dp ,那么第 j j 位可以填数的位置数应当为 m i n { i , j } + 2 min\{i,j\}+2 ,保留余数不超过 ( i + 2 ) k (i+2)k 的状态即可。
  • 上述做法需要进行 O ( L o g N ) O(LogN) d p dp ,但压位后已经可以通过。
  • 注意到 m i n { i , j } + 2 min\{i,j\}+2 j j 较小时只与 j j 有关, j j 较大时的 d p dp 值就是隔板法形成的组合数,可以避免进行 O ( L o g N ) O(LogN) d p dp ,改为从低位向高位进行一次 d p dp
  • 不计高精度运算的时间复杂度为 O ( K 2 L o g 3 N ) O(K^2Log^3N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
const int MAXNK = 605;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
const int MAXP = 205, P = 1e9;
struct INT {
	int len, a[MAXP];
	INT () {
		len = 1;
		memset(a, 0, sizeof(a));
	}
	INT (int num) {
		assert(num < P);
		len = 1;
		memset(a, 0, sizeof(a));
		a[0] = num;
	}
};
bool operator < (const INT &x, const INT &y) {
	if (x.len < y.len) return true;
	if (x.len > y.len) return false;
	for (int i = x.len - 1; i >= 0; i--) {
		if (x.a[i] < y.a[i]) return true;
		if (x.a[i] > y.a[i]) return false;
	}
	return false;
}
bool operator > (const INT &x, const INT &y) {
	if (x.len > y.len) return true;
	if (x.len < y.len) return false;
	for (int i = x.len - 1; i >= 0; i--) {
		if (x.a[i] > y.a[i]) return true;
		if (x.a[i] < y.a[i]) return false;
	}
	return false;
}
bool operator <= (const INT &x, const INT &y) {
	if (x.len < y.len) return true;
	if (x.len > y.len) return false;
	for (int i = x.len - 1; i >= 0; i--) {
		if (x.a[i] < y.a[i]) return true;
		if (x.a[i] > y.a[i]) return false;
	}
	return true;
}
bool operator >= (const INT &x, const INT &y) {
	if (x.len > y.len) return true;
	if (x.len < y.len) return false;
	for (int i = x.len - 1; i >= 0; i--) {
		if (x.a[i] > y.a[i]) return true;
		if (x.a[i] < y.a[i]) return false;
	}
	return true;
}
bool operator == (const INT &x, const INT &y) {
	if (x.len != y.len) return false;
	for (int i = x.len - 1; i >= 0; i--)
		if (x.a[i] != y.a[i]) return false;
	return true;
}
bool operator != (const INT &x, const INT &y) {
	if (x.len != y.len) return true;
	for (int i = x.len - 1; i >= 0; i--)
		if (x.a[i] != y.a[i]) return true;
	return false;
}
INT operator + (const INT &x, const INT &y) {
	INT res;
	res.len = max(x.len, y.len) + 1;
	for (int i = 0; i < res.len; i++) {
		res.a[i] += x.a[i] + y.a[i];
		if (res.a[i] >= P) {
			res.a[i + 1] += res.a[i] / P;
			res.a[i] %= P;
		}
	}
	while (res.len >= 2 && res.a[res.len - 1] == 0) res.len--;
	return res;
}
INT operator += (INT &x, const INT &y) {
	x = x + y;
	return x;
}
INT operator - (const INT &x, const INT &y) {
	INT res; assert(x >= y);
	res.len = x.len;
	for (int i = 0; i < res.len; i++) {
		res.a[i] += x.a[i] - y.a[i];
		if (res.a[i] < 0) {
			res.a[i + 1] += res.a[i] / P;
			res.a[i] %= P;
			if (res.a[i] < 0) {
				res.a[i + 1] -= 1;
				res.a[i] += P;
			}
		}
	}
	while (res.len >= 2 && res.a[res.len - 1] == 0) res.len--;
	return res;
}
INT operator -= (INT &x, const INT &y) {
	x = x - y;
	return x;
}
INT operator * (const INT &x, const INT &y) {
	INT res;
	res.len = x.len + y.len;
	static long long tres[MAXP];
	memset(tres, 0, sizeof(tres));
	for (int i = 0; i < x.len; i++)
	for (int j = 0; j < y.len; j++) {
		long long tmp = 1ll * x.a[i] * y.a[j];
		tres[i + j] += tmp % P;
		tres[i + j + 1] += tmp / P;
	}
	for (int i = 0; i < res.len; i++) {
		tres[i + 1] += tres[i] / P; 
		res.a[i] = tres[i] % P;
	}
	while (res.len >= 2 && res.a[res.len - 1] == 0) res.len--;
	return res;
}
INT operator *= (INT &x, const INT &y) {
	x = x * y;
	return x;
}
INT operator / (const INT &x, const int &y) {
	INT res; ll rem = 0;
	res.len = x.len;
	for (int i = x.len - 1; i >= 0; i--) {
		rem += x.a[i];
		res.a[i] = rem / y;
		rem = rem % y * P;
	}
	while (res.len >= 2 && res.a[res.len - 1] == 0) res.len--;
	return res;
}
INT operator /= (INT &x, const int &y) {
	x = x / y;
	return x;
}
int operator % (const INT &x, const int &y) {
	int ans = x.a[x.len - 1] % y;
	for (int i = x.len - 2; i >= 0; i--)
		ans = (1ll * P * ans + x.a[i]) % y;
	return ans;
}
INT operator %= (INT &x, const int &y) {
	x = x % y;
	return x;
}
void print(const INT &x) {
	printf("%d", x.a[x.len - 1]);
	for (int i = x.len - 2; i >= 0; i--)
		printf("%09d", x.a[i]);
	printf("\n");
}
INT f[MAXN][MAXNK];
INT dp[MAXN][MAXNK];
INT n, res[MAXN];
int k, Log, a[MAXN];
void getdp() {
	f[0][0] = 1;
	for (int i = 1; i <= Log + 2; i++)
	for (int j = 0; j <= i * (k - 1); j++)
	for (int l = 0; l <= k - 1 && l <= j; l++)
		f[i][j] += f[i - 1][j - l];
	dp[0][0] = 1;
	for (int i = 0; i <= Log - 1; i++)
	for (int j = 0; j <= (i + 2) * k; j++) {
		for (int l = 0; l <= (i + 2) * (k - 1) && l <= j; l++)
			if ((j - l) * k + a[i] <= (i + 2) * k) dp[i + 1][j] += dp[i][(j - l) * k + a[i]] * f[(i + 2)][l];
	}
}
INT binom(INT a, int b) {
	INT ans = 1; int tmp = 1;
	for (int i = 1; i <= b; i++)
		ans *= (a - (i - 1));
	for (int i = 1; i <= b; i++) {
		if (1ll * tmp * i >= P) ans /= tmp, tmp = i;
		else tmp *= i;
	}
	return ans / tmp;
}
INT getans(int n) {
	INT ans = 0, lft = 0;
	for (int i = Log; i >= n; i--)
		lft = lft * k + a[i];
	for (int i = 0; i <= (n + 2) * k && i <= lft; i++)
		if ((lft - i) % k == 0) ans += dp[n][i] * binom((lft - i) / k + n + 1, n + 1);
	return ans;
}
int main() {
	read(k), read(n);
	Log = 0;
	while (n != 0) {
		a[++Log] = n % k;
		n /= k;
	}
	getdp();
	INT last = 1;
	for (int i = 0; i <= Log - 1; i++) {
		res[i] = getans(i);
		print(res[i] - last);
		last = res[i];
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/89788875