2026-07-20
CF
Problem - 1794D - Codeforces
数学推导,方案数dp
dp[j]: 从已经处理过的不同质数中选择 j 个, 所有选择集合的 cnt 乘积之和。
![[Pasted image 20260720162635.png|400]]
cnt[x] 是对剩余指数排列数的修正,不是在区分相同的质数副本。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
//#define double long double
#define endl '\n'
#define ULL unsigned long long
#define PII pair<int, int>
const LL mod = 998244353;
const int N = 3010;
const int M = 1e6 + 10;
LL f[M], inv[M];
LL dp[N];
void init(){//阶乘板子
int n = 1000000;
f[0] = f[1] = inv[0] = inv[1] = 1;
for (int i = 2; i <= n; i++)
{ // 求阶乘和单个数逆元
f[i] = f[i - 1] * i % mod;
inv[i] = mod - (mod / i) * inv[mod % i] % mod;
}
for (int i = 1; i <= n; i++)
{ // 求阶乘逆元
inv[i] = inv[i - 1] * inv[i] % mod;
}
}
bool st[M];
int prime[M];
int cnt;
void get_prime(int n)
{
st[1] = true;
for (int i = 2; i <= n;i++){
if(!st[i])
prime[cnt++] = i;
for (int j = 0; prime[j] <= n / i;j++){
st[prime[j] * i] = true;
if(i%prime[j]==0)
break;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init();
get_prime(1000000);
int n;
cin >> n;
map<int, int> mp;
for (int i = 1; i <= 2*n;i++){
int x;
cin >> x;
mp[x]++;
}
dp[0] = 1;
for(auto [x,cnt]:mp){
if(st[x]){//合数跳过
continue;
}
for (int j = n; j >= 1;j--){
dp[j] = (dp[j] + dp[j - 1] * cnt) % mod;
}
}
LL ans = dp[n] * f[n] % mod;
for(auto [x,cnt]:mp){
ans = ans * inv[cnt] % mod;
}
cout << ans << endl;
}
Problem - 721C - Codeforces
两个方法时间复杂度都是 \(O(nm)\)
拓扑排序+dp
先枚举节点 u
再枚举经过的景点数 k
再枚举 u 的出边
它依赖拓扑序保证:
处理节点 u 时,所有能够到达 u 的前驱节点都已经处理完了。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
//#define double long double
#define endl '\n'
#define ULL unsigned long long
#define PII pair<int, int>
const LL mod = 998244353;
const int N = 5010;
struct edge{
int v,w;
};
vector<edge> e[N];
int in[N];
int dp[N][N], pre[N][N];
//dp[v][k]:1->v,经过 k 个点
void dfs(int u,int len){
if(pre[u][len]){
dfs(pre[u][len], len - 1);
}
cout << u << " ";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, mxt;
cin >> n >> m >> mxt;
for (int i = 1; i <= m;i++){
int u,v,w;
cin >> u >> v >> w;
e[u].push_back({v, w});
in[v]++;
}
queue<int> q;
for (int i = 1; i <= n;i++){
if(in[i]==0){
q.push(i);
}
}
vector<int> topo;
while(!q.empty()){
int u = q.front();
q.pop();
topo.push_back(u);
for(auto nxt:e[u]){
int v = nxt.v;
in[v]--;
if(in[v]==0){
q.push(v);
}
}
}
memset(dp, 0x3f, sizeof dp);
dp[1][1]=0;
//O(nm)
for(auto u:topo){
for (int k = 1; k < n;k++){
if(dp[u][k]>mxt){
continue;
}
for(auto [v,w]:e[u]){
int nxt = dp[u][k] + w;
if(nxt<=mxt&&nxt<dp[v][k+1]){
dp[v][k + 1] = nxt;
pre[v][k + 1] = u;
}
}
}
}
int tot = n;
while(dp[n][tot]>mxt){
tot--;
}
cout << tot << endl;
dfs(n, tot);
}
分层 DP
先枚举经过的景点数 k
再枚举所有边
用 dp[v][k] 表示从 1 到 v 恰好经过 k 个景点的最短时间,按景点数量逐层枚举所有边转移,最后找最大的 k 满足 dp[n][k] ≤ T,并用前驱数组还原路径。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
//#define double long double
#define endl '\n'
#define ULL unsigned long long
#define PII pair<int, int>
const LL mod = 998244353;
const int N = 5010;
struct edge{
int u,v,w;
};
vector<edge> etot;
int in[N];
int dp[N][N], pre[N][N];
//dp[v][k]:1->v,经过 k 个点
void dfs(int u,int len){
if(pre[u][len]){
dfs(pre[u][len], len - 1);
}
cout << u << " ";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, mxt;
cin >> n >> m >> mxt;
for (int i = 1; i <= m;i++){
int u,v,w;
cin >> u >> v >> w;
etot.push_back({u, v, w});
}
memset(dp, 0x3f, sizeof dp);
dp[1][1]=0;
for (int k = 1; k < n;k++){//分层 dp,写法更简洁一点
for(auto [u,v,w]:etot){
if(dp[u][k]>mxt){
continue;
}
int nxt = dp[u][k] + w;
if(nxt<=mxt&&nxt<dp[v][k+1]){
dp[v][k + 1] = nxt;
pre[v][k + 1] = u;
}
}
}
int tot = n;
while(dp[n][tot]>mxt){
tot--;
}
cout << tot << endl;
dfs(n, tot);
}
Problem - 78C - Codeforces
设 \(p\) 是 \(m\) 的最小大于 1 的约数。
Timur 获胜必须同时满足:
\[n 是奇数
\]
并且:
\[m>1
\]
并且:
\[\frac{m}{p}\ge k
\]
否则 Marsel 获胜。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
//#define double long double
#define endl '\n'
#define ULL unsigned long long
#define PII pair<int, int>
const LL mod = 998244353;
const int N = 2e5+10;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, k;
cin >> n >> m >> k;
if(n%2==0){
cout << "Marsel\n";
return 0;
}
if(m==1){
cout << "Marsel\n";
return 0;
}
LL p = m;
for (int i = 2; i <= m / i;i++){
if(m%i==0){
p = i;
break;
}
}
if(m/p>=k){
cout << "Timur\n";
}else{
cout << "Marsel\n";
}
}

浙公网安备 33010602011771号