返回顶部

AtCoder Beginner Contest 212 E - Safety Journey (DP)

  • 题意:一张\(n\)个点的完全图,删去\(m\)条边,一共走\(k\)步,问你从起点出发再回到起点一共有多少种方案?

  • 题解:设\(dp[i][j]\)表示走了\(i\)步,以\(j\)为终点的方案数,那么状态转移为\(dp[i+1][u]=\sum_{v\in edge[u]} dp[i][v]\).

    此时的代码为(注意此时的\(edge\)表示连着的边):

    for(int i=0;i<k;++i){
        for(int u=1;u<=n;++u){
            for(auto v:edge[u]){
                dp[i+1][u]+=dp[i][v];
            }
        }
    }
    

    因为是完全图,所以时间复杂度为\(O(k*n^2)\).

    但题目给的是删去的边,我们可以将原方程转化一下(此时的\(edge\)表示删去的边):\(dp[i+1][u]=\sum_{v=1}^{n}dp[i][v]-\sum _{v\in edge[u]}dp[i][v]\).因为删去的边最多只有\(5000\)条,所以此时的时间复杂度为\(O((n+m)*k)\).

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod =998244353;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int n,m,k;
    vector<int> edge[N];
    ll dp[5005][5005];
    
    int main() {
        scanf("%d %d %d",&n,&m,&k);
        for(int i=1;i<=m;++i){
            int u,v;
            scanf("%d %d",&u,&v);
            edge[u].pb(v),edge[v].pb(u);
        }
        dp[0][1]=1;
        for(int i=0;i<k;++i){
            ll sum=0;
            for(int j=1;j<=n;++j) sum=(sum+dp[i][j]+mod)%mod;
            for(int j=1;j<=n;++j){
                dp[i+1][j]=(sum-dp[i][j]+mod)%mod;
                for(auto to:edge[j]){
                    dp[i+1][j]=(dp[i+1][j]-dp[i][to]+mod)%mod;
                }
            }
        }
        printf("%lld\n",dp[k][1]);
        return 0;
    }
    
    
posted @ 2021-09-08 19:39  Rayotaku  阅读(107)  评论(1编辑  收藏  举报