Wannafly挑战赛22 字符路径Dp

题目链接
题目描述

给一个含n个点m条边的有向无环图(允许重边,点用1到n的整数表示),每条边上有一个字符,问图上有几条路径满足路径上经过的边上的字符组成的的字符串去掉空格后以大写字母开头,句号
‘.’ 结尾,中间都是小写字母,小写字母可以为0个

样例

6 11
1 2 A
1 2 _
3 4 _
2 4 B
2 3 a
2 3 _
2 4 b
4 5 .
3 5 .
2 5 .
5 6 _
16

思路

dp 一开始想的时候是用dp分别记录到这个点以空格结尾, 以大写字母结尾,以小写字母结尾和以.结尾的路径条数。但是不对,主要是空格的影响太大了。
看了题解,dp数组表示的很巧妙。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e5 + 100;

struct node {
   int v;
   char c;
};

int vis[maxn];
vector<node> Edge[maxn];
queue<int> q;

const ll mod = ((ll)1<<32);
ll dp[maxn][3], ans;

//dp[i][0] 空格的个数
//dp[i][1] 这条路上还没有到.的路径的个数
//dp[i][2] 这条路上以.结尾的路径条数

void bfs() {
    ans = 0;
   while(!q.empty()) {
      int now = q.front();
      q.pop();

      ans += dp[now][2];
      ans %= mod;

      for(int i = 0; i < Edge[now].size(); i++) {
          int to = Edge[now][i].v;
          char c = Edge[now][i].c;
          vis[to]--;

          if(c == '_') {
             dp[to][0] += dp[now][0] + 1;
             dp[to][1] += dp[now][1];
             dp[to][2] += dp[now][2];
          }
          else if(c >= 'A' && c <= 'Z') {
            dp[to][1] += dp[now][0] + 1;
          }
          else if(c == '.') {
            dp[to][2] += dp[now][1];
          }
          else dp[to][1] += dp[now][1];

          dp[to][0] %= mod;
          dp[to][1] %= mod;
          dp[to][2] %= mod;

          if(!vis[to]) {
            q.push(to);
          }

//          printf("%d %lld %lld %lld\n", to, dp[to][0], dp[to][1], dp[to][2]);
      }
   }
}

int main()
{
   int n, m;
   scanf("%d %d", &n, &m);

   for(int i = 1; i <= m; i++) {
       int u, v;
       char s[10];
       scanf("%d %d %s", &u, &v, s);
       Edge[u].push_back((node) {v, s[0]});
       vis[v]++;
   }

   for(int i = 1; i <= n; i++) {
      if(!vis[i]) q.push(i);
   }

   bfs();
   printf("%lld\n", ans);
}

猜你喜欢

转载自blog.csdn.net/deerly_/article/details/81983414
今日推荐