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";
    }
}
posted @ 2026-07-20 19:38  Seren_blingbling  阅读(10)  评论(0)    收藏  举报