【计数DP】CF1794D

116 篇文章 0 订阅
37 篇文章 0 订阅

Problem - D - Codeforces

题意

思路

解法大方向对了,但是还是不会做,原因是组合数不知道怎么求

首先需要注意到一些东西:

1.底数一定是质数

2.质数个数 < n 一定无解

3.哪些质数作为底数是不确定的

4.n <= 2022

那么我们其实可以把做法大致猜出来

根据第4点,应该是个二维的dp,且状态的设计感觉非常唯一:

设 dp[i][j] 表示前 i 个数,已经选了 j 个数作为底数的方案数

然后就是考虑转移,这种计数类的dp在转移的时候都是考虑多一格会多出多少贡献,贡献一般由组合数求出

问题就是出在这里,我不知道怎么求组合数,一心想着对于指数求可重集排列,但是这么多的指数的个数是很难维护的

其实应该考虑分配,算出组合就行了,不用去考虑排列

设多出来那一格的数为 x, 它的个数为 y

那就是考虑把这 y 个 x 分配到 指数中,指数中还剩余多少个空位呢

前缀已经选了 j 个底数,设前缀的个数和为 sum,那么前缀有 sum - j 个数作为指数

所以还剩下 n - (sum - j) 个位置,也就是说,在 n - (sum - j) 个位置中选 y 个位置,那就是 C(n - (sum - j), y)

如果作为底数也是同样的道理

对 x 作为指数还是底数讨论一下即可

Code:

#include <bits/stdc++.h>

#define int long long

constexpr int N = 1e6 + 10;
constexpr int mod = 998244353;
constexpr int Inf = 0x3f3f3f3f;

int n, x;
int len = 0;
int Fac[N];
int inv[N];
int vis[N], prime[N];

int qpow(int a, int b) {
	int res = 1;
	while(b) {
		if (b & 1) res = (res * a) % mod;
		a = (a * a) % mod;
		b >>= 1;
	}
	return res;
}
int C(int n, int m) {
	if (n < 0 || m < 0 || n < m) return 0;
	return Fac[n] * inv[m] % mod * inv[n - m] % mod;
}
void Fac_init() {
	Fac[0] = 1;
	for (int i = 1; i < N; i ++) {
		Fac[i] = (Fac[i - 1] * i) % mod;
	}
	inv[N - 1] = qpow(Fac[N - 1], mod - 2);
	for (int i = N - 2; i >= 0; i --) {
		inv[i] = inv[i + 1] * (i + 1) % mod;
	}
}
void P_init(int n) {
	vis[1] = 1;
	for (int i = 2; i <= n; i ++) {
		if (!vis[i]) prime[++len] = i;
		for (int j = 1; i <= n / prime[j]; j ++) {
			vis[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}
void solve() {
	std::cin >> n;

	std::map<int, int> mp;
	for (int i = 1; i <= 2 * n; i ++) {
		std::cin >> x;
		mp[x] += 1;
	}

	int sum = 0;
	std::vector<int> dp(n + 1, 0);
	dp[0] = 1;
	for (auto [x, y] : mp) {
		std::vector<int> ndp(n + 1, 0);
		for (int j = 0; j <= n; j ++) {
			ndp[j] += dp[j] * C(n - (sum - j), y) % mod;
			ndp[j] %= mod;
			if (j >= 1 && !vis[x]) {
				ndp[j] += dp[j - 1] * C(n - (sum - j + 1), y - 1) % mod;
				ndp[j] %= mod;
			}
		}
		dp.swap(ndp);
		sum += y;
		sum %= mod;
	}
	
	std::cout << dp[n] % mod << "\n";
}
signed main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int t = 1;
	Fac_init();
	P_init(1e6);
	while (t--) {
		solve();
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用D触发器实现四进制计数的方法如下: ```verilog module count4(clk, reset, d, q); input clk, reset; input \[1:0\] d; output \[1:0\] q; reg \[1:0\] q; always @(posedge clk or negedge reset) begin if (~reset) begin q <= 2'b00; end else begin q <= d; end end endmodule ``` 在这个模块中,我们使用了一个D触发器来实现四进制计数。输入信号`d`是一个两位的二进制数,表示下一个计数的值。输出信号`q`是一个两位的二进制数,表示当前的计数值。 在时钟上升沿或复位信号下降沿时,根据复位信号的状态,我们将`q`的值设置为0或者`d`的值。当复位信号为低电平时,我们将`q`设置为0,表示重新开始计数。当复位信号为高电平时,我们将`q`设置为`d`的值,表示继续计数。 这样,我们就可以使用D触发器来实现四进制计数。 #### 引用[.reference_title] - *1* *2* [jk触发器改为四进制_四位二进制计数器这样接成十进制计数器](https://blog.csdn.net/weixin_39598094/article/details/110070401)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [(36)FPGA面试题D触发器实现4进制计数器](https://blog.csdn.net/m0_46498597/article/details/116426325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值