2022 年 1 月 13 日

CF27E Number With The Given Amount Of Divisors

即把 \(n\) 分解成 \((k_1+1)(k_2+1)\dots(k_n+1)\) 的形式,配给一些质数作为指数,使得 \(\prod p_i^{k_i}\) 最小。暴力搜索即可。

有一个小性质:可以发现 \(2\) 一定是最小答案的质因数,且其质因数的指数随底数的增大而不增。

#include<cstdio>
#include<algorithm>
#include<stack>
int vis[60001], prime[60001], tot, pps[1001]; long long pp[1001][1001];
long long ans = 0x7fffffffffffffffll;
void solve(int i, int n, long long cur) {
	if (cur > ans) return; if (i > 1000) return;
	if (n == 1) return void(ans = cur);
	std::stack<int> lst;
	for (int j = 1; j * j <= n; j++) {
		if (n % j == 0) {
			if (j * j != n && j != 1) lst.push(j);
			int k = n / j - 1;
			if (k > pps[i]) continue;
			if (1e18 / cur <= pp[i][k]) continue;
			solve(i + 1, j, cur * pp[i][k]);
		}
	}
	while (!lst.empty()) {
		int k = lst.top() - 1, j = lst.top(); lst.pop();
		if (k > pps[i]) continue;
		if (1e18 / cur <= pp[i][k]) continue;
		solve(i + 1, n / j, cur * pp[i][k]);
	}
}
int main() {
	for (int i = 2; i <= 60000; i++) {
		if (!vis[i]) prime[++tot] = i;
		for (int j = 1; j <= tot; j++) {
		    int x = prime[j];
			if (i * x > 60000) break;
			vis[i * x] = 1; if (i % x == 0) break;
		}
	}
	for (int i = 1; i <= 1000; i++) {
		pp[i][0] = 1, pp[i][1] = prime[i], pps[i] = 1;
		while (1e18 / pp[i][pps[i]] >= prime[i]) pps[i]++, pp[i][pps[i]] = pp[i][pps[i] - 1] * prime[i];
	}
	int n; scanf("%d", &n), solve(1, n, 1);
	printf("%lld\n", ans);
}

洛谷 P2054 [AHOI2005]洗牌

\(f(x)\) 为处于位置 \(x\) 的数在一次洗牌后的位置。有:

\[f(x)=\begin{cases} 2x&x\leq\frac n2\\ 2\left(x-\frac n2-1\right)+1&x\gt\frac n2 \end{cases} \]

\[f(x)=\begin{cases} 2x&x\leq\frac n2\\ 2x-n-1&x\gt\frac n2 \end{cases} \]

在模 \(n+1\) 意义下可以表示为 \(f(x)\equiv 2x\)。那么经过 \(m\) 次洗牌后数字 \(x\) 位于 \(2^mx\bmod (n+1)\) 的位置。

则问题转化成了求方程 \(2^mx\equiv L\pmod{n+1}\) 的最小正整数解。

#include<cstdio>
typedef long long ll;
ll n, m, L;
ll mul(ll x, ll y) {
	__int128 t = ((__int128)x) * y;
	return (ll)(t % (n + 1));
}
ll pow(ll x, ll y) {
	ll ret = 1;
	while (y) {
		if (y & 1) ret = mul(ret, x);
		x = mul(x, x), y >>= 1;
	}
	return ret;
}
ll exgcd(ll a, ll b, ll& x, ll& y) {
	if (!b) return x = 1, y = 0, a;
	ll d = exgcd(b, a % b, y, x);
	return y -= (a / b) * x, d;
}
ll inv(ll a) {
	ll ret = 0, x = 0; exgcd(a, n + 1, ret, x);
	return (ret % (n + 1) + (n + 1)) % (n + 1);
}
int main() {
	scanf("%lld%lld%lld", &n, &m, &L);
	printf("%lld\n", mul(L, inv(pow(2ll, m))));
}

洛谷 P2618 数字工程

直接利用埃筛处理出所有的数能变成的数,然后 DP 即可。

#include<cstdio>
#include<vector>
#include<algorithm>
using std::vector; using std::min;
int vis[1000006], n, f[1000006]; vector<int> e[1000006];
int main() {
	for (int i = 2; i <= 1000000; i++) {
		if (vis[i]) continue; e[i].push_back(1);
		for (int j = i * 2; j <= 1000000; j += i) {
			vis[j] = 1; e[j].push_back(j / i);
		}
	}
	for (int i = 2; i <= 1000000; i++) {
		f[i] = f[i - 1] + 1;
		for (int j : e[i]) f[i] = min(f[i], f[j] + 1);
	}
	int n;
	while (~scanf("%d", &n)) printf("%d\n", f[n]);
}

UVA1642 魔法GCD Magical GCD

对于所有以某一个数为右端点的所有区间,其 \(\gcd\) 随区间长度的增大而不增。且每次减少至少减少一半,也就是说,最多有 \(\log a_i\) 种取值。

从左到右枚举右端点,维护所有不同 \(\gcd\) 对应区间的左端点,并更新答案即可。

#include<cstdio>
#include<deque>
#include<algorithm>
typedef long long ll; using std::max;
ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
std::deque<int> q[2]; ll f[100005];
void solve() {
	int n; ll ans = 0; scanf("%d", &n);
	while (!q[0].empty()) q[0].pop_back();
	while (!q[1].empty()) q[1].pop_back();
	for (int i = 1, d = 0; i <= n; i++, d ^= 1) {
		ll x; scanf("%lld", &x);
		ans = max(ans, x);
		while (!q[d].empty()) {
			int j = q[d].front(); q[d].pop_front();
			f[j] = gcd(f[j], x), ans = max(ans, f[j] * (i - j + 1));
			if (q[d ^ 1].empty() || f[q[d ^ 1].back()] != f[j]) q[d ^ 1].push_back(j);
		}
		if (q[d ^ 1].empty() || f[q[d ^ 1].back()] != x) q[d ^ 1].push_back(i);
		f[i] = x;
	}
	printf("%lld\n", ans);
}

int main() {
	int T; scanf("%d", &T);
	while (T--) solve();
}

UVA10791 最小公倍数的最小和 Minimum Sum LCM

\(n\) 分解质因数。

\(n=p_1^{k_1}p_2^{k_2}\dots p_m^{k_m}\),则答案为 \(\sum p_i^{k_i}\)

特判 \(m\lt 2\) 的情况。

#include<cstdio>
int main() {
	int n, T = 0;
	while (T++, scanf("%d", &n), n) {
		int cnt = 0; long long sum = (n == 1);
		for (int i = 2; 1ll * i * i <= n; i++) {
			if (n % i != 0) continue;
			int cur = 1;
			while (n % i == 0) cur *= i, n /= i;
			sum += cur, cnt++; 
		}
		if (n != 1) sum += n, cnt++;
		if (cnt <= 1) sum++;
		printf("Case %d: %lld\n", T, sum);
	}
}

洛谷 P2714 四元组统计

\(f(x,y)\) 表示选择 \(x\) 个数,它们含有公因数 \(y\) 的方案数。

插入新数时枚举新数的所有因子,并更新 \(f(x,y)\)

最后计算答案时直接倒推即可。

#include<cstdio>
typedef long long ll;
ll f[5][10004];
void insert(int n) {
	for (int i = 1; i * i <= n; i++) {
		if (n % i) continue;
		f[4][i] += f[3][i], f[3][i] += f[2][i], f[2][i] += f[1][i], f[1][i]++;
		if (i * i != n) f[4][n / i] += f[3][n / i], f[3][n / i] += f[2][n / i], f[2][n / i] += f[1][n / i], f[1][n / i]++;
	}
}
int main() {
	int n;
	while (~scanf("%d", &n)) {
		for (int i = 1; i <= n; i++) {
			int x; scanf("%d", &x), insert(x);
		}
		for (int i = 10000; i >= 1; i--) {
			for (int j = 2 * i; j <= 10000; j += i) f[4][i] -= f[4][j];
		}
		printf("%lld\n", f[4][1]);
		for (int i = 1; i <= 10000; i++) f[1][i] = f[2][i] = f[3][i] = f[4][i] = 0;
	}
}

UVA12775 Gift Dilemma

求三元一次不定方程 \(Ax+By+Cz=D\) 的自然数解个数。

先判无解的情况。然后将 \(A,B,C,D\) 除去 \(\gcd(A,B,C)\)

由于题目保证 \(\frac C{\gcd(A,B,C)}\geq 200\),所以可以选择枚举 \(z\),然后将原方程转化为二元一次不定方程求解。

#include<cstdio>
typedef long long ll;
int gcd(int x, int y) { return y == 0 ? x : gcd(y, x % y); }
int gcd(int x, int y, int z) { return gcd(x, gcd(y, z)); }
int exgcd(int a, int b, ll& x, ll& y) {
	if (!b) return x = 1, y = 0, a;
	int d = exgcd(b, a % b, y, x);
	return y -= (a / b) * x, d;
}
int main() {
	int T; scanf("%d", &T);
	for (int _ = 1; _ <= T; _++) {
		long long ans = 0;
		int A, B, C, D; scanf("%d%d%d%d", &A, &B, &C ,&D);
		if (D % gcd(A, B, C)) { printf("Case %d: 0\n", _); continue; }
		int d = gcd(A, B, C); A /= d, B /= d, C /= d, D /= d;
		for (int z = 0; C * z <= D; z++) {
			int c = D - C * z; ll x0, y0;
			int d = exgcd(A, B, x0, y0);
			if (c % d) continue;
			x0 *= c / d, y0 *= c / d;
			ll dx = B / d, dy = A / d;
			if (x0 < 0) {
				ll offset = (-x0 / dx) + ((-x0) % dx != 0);
				x0 += offset * dx, y0 -= offset * dy;
			} else if (x0 > 0) {
				ll offset = x0 / dx;
				x0 -= offset * dx, y0 += offset * dy;
			}
			if (y0 < 0) continue;
			ll offset = y0 / dy;
			ans += offset + 1;
		}
		printf("Case %d: %lld\n", _, ans);
	}
}

洛谷 P3961 [TJOI2013]黄金矿工

将所有在同一射线上的点按照到原点的距离从小到大排序,进行前缀和处理,将问题转化为分组背包。

#include<cstdio>
#include<vector>
#include<utility>
#include<algorithm>
using std::pair; using std::make_pair; using std::vector;
vector<pair<int, int>> a[500][200];
int n, x[1000], y[1000], p[1000], c[1000], v[1000], t, f[2][40004];
int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
int _(int x) { return x < 0 ? -x : x; }
int main() {
	scanf("%d%d", &n, &t);
	for (int i = 1; i <= n; i++) scanf("%d%d%d%d", x + i, y + i, c + i, v + i), p[i] = i;
	std::sort(p + 1, p + n + 1, [=](int a, int b) { return _(x[a]) < _(x[b]); });
	for (int i = 1; i <= n; i++) {
		int d = gcd(_(x[p[i]]), y[p[i]]), dx = x[p[i]] / d, dy = y[p[i]] / d;
		if (!a[dx + 200][dy].size()) a[dx + 200][dy].push_back(make_pair(c[p[i]], v[p[i]]));
		else {
			auto _p = *a[dx + 200][dy].rbegin();
			a[dx + 200][dy].push_back(make_pair(c[p[i]] + _p.first, v[p[i]] + _p.second));
		}
	}
	int cnt = 0;
	for (int dx = 0; dx <= 400; dx++) {
		for (int dy = 0; dy <= 200; dy++) {
			if (!a[dx][dy].size()) continue;
			cnt++; int d = (cnt & 1);
			for (int i = 0; i <= t; i++) f[d][i] = f[d ^ 1][i];
			for (auto p : a[dx][dy]) {
				for (int i = t; i >= p.first; i--) {
					f[d][i] = std::max(f[d][i], f[d ^ 1][i - p.first] + p.second);
				}
			}
		}
	}
	printf("%d\n", f[cnt & 1][t]);
}

洛谷 P4799 [CEOI2015 Day2]世界冰球锦标赛

\(n\) 张门票分为两组,分别对每一组枚举每一种方案计算总花费,最后将两组的总花费序列分别排序,二分合并答案。

#include<cstdio>
#include<algorithm>
using std::sort; using std::lower_bound;
typedef long long ll;
ll L[2000006], R[2000006], m, a[50];
int n;
int main() {
	scanf("%d%lld", &n, &m);
	for (int i = 1; i <= n; i++) scanf("%lld", a + i);
	if (n == 1) return printf("%d\n", (a[1] <= m) + 1), 0;
	int ln = n / 2, rn = n - ln;
	for (int i = 0; i < (1 << ln); i++) {
		for (int j = 1; j <= ln; j++) {
			if ((i >> (j - 1)) & 1) L[i] += a[j];
		}
	}
	for (int i = 0; i < (1 << rn); i++) {
		for (int j = ln + 1; j <= n; j++) {
			if ((i >> (j - ln - 1)) & 1) R[i] += a[j];
		}
	}
	std::sort(L, L + (1 << ln));
	std::sort(R, R + (1 << rn));
	ll ans = 0;
	for (int i = 0; i < (1 << ln); i++) {
		if (L[i] > m) break;
		ans += std::upper_bound(R, R + (1 << rn), m - L[i]) - R;
	}
	printf("%lld\n", ans);
}
posted @ 2022-01-13 19:44  e3c8f1a924  阅读(168)  评论(0)    收藏  举报