AtCoder Beginner Contest 372 F - Teleporting Takahashi 2

F - Teleporting Takahashi 2 

Time Limit: 3 sec / Memory Limit: 1024 MB

Score : 525 points

Problem Statement

There is a simple directed graph G with N vertices and N+M edges. The vertices are numbered 1 to N, and the edges are numbered 1 to N+M.

Edge i (1≤i≤N) goes from vertex i to vertex i+1. (Here, vertex N+1 is considered as vertex 1.)
Edge N+i (1≤i≤M) goes from vertex Xi​ to vertex Yi​.

Takahashi is at vertex 1. At each vertex, he can move to any vertex to which there is an outgoing edge from the current vertex.

Compute the number of ways he can move exactly K times.

That is, find the number of integer sequences (v0,v1,…,vK) of length K+1 satisfying all of the following three conditions:

  • 1≤vi≤N for i=0,1,…,K.
  • v0=1.
  • There is a directed edge from vertex vi−1​ to vertex vi for i=1,2,…,K.

Since this number can be very large, print it modulo 998244353.

Constraints

  • 2≤N≤2×10^5
  • 0≤M≤50
  • 1≤K≤2×10^5
  • 1≤Xi,Yi≤N, Xi≠Yi
  • All of the N+M directed edges are distinct.
  • All input values are integers.

Input

The input is given from Standard Input in the following format:

N M K
X1​ Y1
X2​ Y2
⋮
XM​ YM

Output

Print the count modulo 998244353.


Sample Input 1

6 2 5
1 4
2 5

Sample Output 1

5

sample1

The above figure represents the graph G. There are five ways for Takahashi to move:

  • Vertex 1→ Vertex 2→ Vertex 3→ Vertex 4→ Vertex 5→ Vertex 6
  • Vertex 1→ Vertex 2→ Vertex 5→ Vertex 6→ Vertex 1→ Vertex 2
  • Vertex 1→ Vertex 2→ Vertex 5→ Vertex 6→ Vertex 1→ Vertex 4
  • Vertex 1→ Vertex 4→ Vertex 5→ Vertex 6→ Vertex 1→ Vertex 2
  • Vertex 1→ Vertex 4→ Vertex 5→ Vertex 6→ Vertex 1→ Vertex 4

Sample Input 2

10 0 200000

Sample Output 2

1

Sample Input 3

199 10 1326
122 39
142 49
164 119
197 127
188 145
69 80
6 120
24 160
18 154
185 27

Sample Output 3

451022766

【思路分析】

二维dp+二分+缩点。由于要求方案数,且一定存在一条最大环,可以在这条环上做dp。其中dp[i][j]表示路线以编号i结尾,走过j步的方案数。度为2的节点显然有dp[i+1][j] = dp[i][j-1],其它点模拟即可。

由于n和k较大,即使dp降维也会tle。考虑缩点,将多个度为2的连续点缩点。重新建图,原m条分支需要重新指向新下标,采用前缀+二分寻找新下标。

依次迭代dp,在最后对答案累加时,由于缩点,每个点记录的是该点集的最小编号,显然只累加dp[i][k]是只考虑了一个点。设sz为点集中点总数,则该缩点对答案的贡献为dp[i][k-sz+1]+...+dp[i][k]。

该方法的时间复杂度为O(N+MlogM+MK),空间复杂度为O(N+MK),符合题意。

#include<bits/stdc++.h>

#define i64 long long
#define ll long long

using namespace std;

const i64 MOD = 998244353;

const i64 N = 2e5 + 5;

vector<int> G[N], refl[205];
i64 dp[205][N], E[205], pre[205];
bool ind[N];

void solve() {
    i64 n, m, k;
    cin >> n >> m >> k;
    if (m == 0) {
        cout << 1;
        return;
    }

    //存点
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        G[u].emplace_back(v);
        ind[v] = true;
    }

    //缩点建新图
    i64 idx = 1;
    for (int i = 1; i <= n; ++i, ++idx) {
        if (G[i].empty()&&!ind[i]) {
            while (i <= n && G[i].empty() && !ind[i]) {
                E[idx]++;
                ++i;
            }
            --i;
        } else if (G[i].empty()) {
            E[idx] = 1;
        } else {
            refl[idx].resize(G[i].size());
            copy(G[i].begin(), G[i].end(), refl[idx].begin());
        }
        if (i > n) break;
        pre[idx] = pre[idx - 1] + (E[idx] == 0 ? 1 : E[idx]);
    }

    //重连边
    --idx;
    for (int i = 1; i <= idx; ++i) {
        if (E[i] == 0) {
            for (int &item: refl[i]) {
                item = lower_bound(pre + 1, pre + idx, item) - pre;
            }
        }
    }

    //dp过程
    dp[1][0] = 1;
    for (int i = 1; i <= k; ++i) {
        for (int j = 1; j < idx; ++j) {
            if (E[j] == 0) {
                for (const auto &item: refl[j]) {
                    dp[item][i] += dp[j][i - 1];
                    dp[item][i] %= MOD;
                }
                dp[j + 1][i] += dp[j][i - 1];
                dp[j+1][i] %= MOD;
            } else if (i >= E[j]) {
                dp[j + 1][i] += dp[j][i - E[j]];
                dp[j + 1][i] %= MOD;
            }
        }
        if (E[idx] == 0) {
            for (const auto &item: refl[idx]) {
                dp[item][i] += dp[idx][i - 1];
                dp[item][i] %= MOD;
            }
            dp[1][i] += dp[idx][i - 1];
            dp[1][i] %= MOD;
        } else if (i >= E[idx]) {
            dp[1][i] += dp[idx][i - E[idx]];
            dp[1][i] %= MOD;
        }
    }

    //答案累加
    i64 res = 0;
    for (int i = 1; i <= idx; ++i) {
        if (E[i] == 1 || E[i] == 0) {
            res += dp[i][k];
            res%=MOD;
        }
        else {
            for (int j = max(k - E[i] + 1, 1ll); j <= k; ++j) {
                res += dp[i][j];
                res%=MOD;
            }
        }
        res %= MOD;
    }

    cout << res % MOD;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
//    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值