2024/11/28 NOIP 模拟赛

数的拆分

Problem

给定 T 个正整数 ai,分别问每个 ai 能否表示为 x1y1x2y2 的形式,其中 x1,x2 为正整数,y1,y2 为大于等于 2 的正整数。

对于所有评测用例,1T100000,1ai1018

Analysis

将原数分解质因数:ai=p1q1p2q2p3q3pnqn。当且仅当所有 qi2 时满足题目条件。但是直接分解复杂度是错误的,考虑仅用 4000 以内的 qi 来分解原数,当且仅当 剩下的数是平方数或立方数 时条件成立。

证明:剩下的数若满足条件,形式只能为 p2p3p2q2 三种(p,q 为质数且 p,q4000)。因为 p2q3>1018 则更大的幂次不成立。则只有平方数或立方数时成立。

Code

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--) 

const int N = 2e5 + 5;
int T, n, prime[N], tot; 

int check(int x) {
	_for(i, 2, x / i) if (x % i == 0) return 0;
	return 1;
}

int Sqr(int x) {
	int l = 1, r = 1e9;
	while (l < r) {
		int mid = (l + r + 1) >> 1;
		if (mid * mid <= x) l = mid;
		else r = mid - 1;
	}
	return (l * l == x);
}

int Cbr(int x) {
	int l = 1, r = 1e6;
	while (l < r) {
		int mid = (l + r + 1) >> 1;
		if (mid * mid * mid <= x) l = mid;
		else r = mid - 1;
	}
	return (l * l * l == x);
}

int read() {
	char c = ' ';
	int f = 1, x = 0;
	while (c < '0' || c > '9') {
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

void wr(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) wr(x / 10);
	putchar(x % 10 + '0');
}

signed main() {
//	freopen("number5.in", "r", stdin);
//	freopen("number5.out", "w", stdout);
	
	cin >> T;
	_for(i, 2, 4000) {
		if (check(i)) prime[++tot] = i;
	}
	while (T--) {
		n = read();
		int flg = 0;
		_for(i, 1, tot) {
			int cnt = 0;
			if (n % prime[i] == 0) {
				while (n % prime[i] == 0) cnt++, n /= prime[i];
				if (cnt == 1) flg = 1;
			}
		}
		if (!flg) {
			if (Cbr(n) || Sqr(n)) puts("yes");
			else puts("no");
		}
		else puts("no");
	}
}

P8863 「KDOI-03」构造数组

Problem

你现在有一个长度为 n 的数组 a。一开始,所有 ai 均为 0。给出一个同样长度为 n 的目标数组 b。求有多少种方案,使得通过若干次以下操作,可以让 a 数组变成 b

  • 选出两个不同的下标 1i<jn,并将 aiaj 同时增加 1

两种方案被称之为不同的,当且仅当存在一个 x 使得一种方案中第 x 次操作选择的两个下标 (i,j) 与另一种方案中的不同。

n5 000bi30 000 |

Analysis

首先这道题部分分是很足的。测试点 15 输出 01。测试点 68 直接暴力 dfs。测试点 914 满足 bi=1,可以找到规律。完成上述部分可以拿到 64 分。

正解:计算出操作次数 m=i=1nbi2。把 m 次操作看成 m 个二元组,然后把 bii 放入这 m 个二元组中,二元组中不考虑顺序,也不能存在相同的数,问填入所有数的方案。考虑转化后的方案如何对应原来的方案,从填入数值层面考虑,发现考虑每个数值填入哪些二元组和方案一一对应:

例如 n=3,b=[1,2,3]

  • [(1,3),(2,3),(2,3)][(1,{1}),(2,{2,3}),(3,{1,2,3})]
  • [(2,3),(1,3),(2,3)][(1,{2}),(2,{1,3}),(3,{1,2,3})]
  • [(2,3),(2,3),(1,3)][(1,{3}),(2,{1,2}),(3,{1,2,3})]

有了上述思路,可以定义 f(i,m1,m2) 表示填完前 i 个数值,存在 m1 个二元组只有一个数,存在 m2 个二元组只有两个数的方案数。可以推算一个数都没填的二元组数量 m0=mm1m2。转移:

f(i+1,m1k+bi+1k,m2+k)=f(i,m1,m2)(m1k)(m0bi+1k)

复杂度 O((n+m)m2)

但是注意到考虑前 i 数值的 m1=j=1ibj2m2。则可以直接删除 m1 这一维。

f(i+1,m2+k)=f(i,m2)(m1k)(m0bi+1k)(m1,m00)

复杂度 O((n+m)m)。因为内层的 k 限制于 bi,则总枚举量为 m

Code

#include <bits/stdc++.h>
using namespace std;

#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
#define int long long

const int N = 5000 + 5, M = 30010, mod = 998244353;

int n, m, a[N], f[2][M], sum[N], fac[M], invfac[M];

int qpow(int a, int k, int p) {
	int res = 1;
	while (k) {
		if (k & 1) res = res * a % p;
		a = a * a % p;
		k >>= 1;
	}
	return res;
}

void init() {
	fac[0] = invfac[0] = 1;
	_for(i, 1, M - 5) {
		fac[i] = fac[i - 1] * i % mod;
		invfac[i] = invfac[i - 1] * qpow(i, mod - 2, mod) % mod;
	}
}

int C(int n, int m) {
	if (n < m || n < 0 || m < 0) return 0;
	return fac[n] * invfac[n - m] % mod * invfac[m] % mod;
}

signed main() {
	cin >> n; init();
	_for(i, 1, n) cin >> a[i], m += a[i], sum[i] = sum[i - 1] + a[i];
	if (m % 2 == 1) puts("0"), exit(0);
	m /= 2;
	f[0][0] = 1;
	_for(i, 0, n - 1) {
		_for(m2, 0, m) {
			int m1 = sum[i] - 2 * m2, m0 = m - m1 - m2;
			if (m0 < 0 || m1 < 0) continue;
			_for(k, 0, a[i + 1]) {
				if (m2 + k <= m) f[(i + 1) & 1][m2 + k] = (f[(i + 1) & 1][m2 + k] + f[i & 1][m2] * C(m1, k) % mod * C(m0, a[i + 1] - k) % mod) % mod;
			}
		}
		memset(f[(i + 2) & 1], 0, sizeof f[(i + 2) & 1]);
	}
	cout << f[n & 1][m] << endl;
}
posted @   Otue  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示