链接:https://ac.nowcoder.com/acm/contest/160/B
来源:牛客网
字符路径
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给一个含n个点m条边的有向无环图(允许重边,点用1到n的整数表示),每条边上有一个字符,问图上有几条路径满足路径上经过的边上的字符组成的的字符串去掉空格后以大写字母开头,句号 '.' 结尾,中间都是小写字母,小写字母可以为0个。
输入描述:
第一行两个整数n,m 接下来m行,每行两个整数a,b和一个字符c,表示一条起点为a,终点为b的边,边上的字符是c 1 ≤ n, m ≤ 50000 1 ≤ a < b ≤ n c可以是大小写字母、句号 '.' 或空格(方便起见用 '_' 表示空格)
输出描述:
输出一个整数,表示答案对232取模的结果
示例1
输入
复制
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
思路:
原本还想保存到达点i的所有字符结尾的状态,这样写好像也可以,但是还有更优秀的方法。
表示到达i点结束,以空格为起点的路径数
表示到达i点结束,以大写字母或者小写字母为中间段的路径数,为什么大写字母归为这种状态呢?因为大写字符+小写字符,和单独的大写字符,状态都是一样的,都可以看成是以空格开头的中间部分。
表示到达i点结束,以句号结尾的路径数。
转移就很好想了,代码中给了注释,可以更好的理解。
举一反三,这里还有一道差不多的状态转移,这种只记录开始,中间和结尾的状态比较常见,值得理解。
Comet oj contest #8:https://blog.csdn.net/Q755100802/article/details/99341358
C题,也是差不多的思路
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,r,l) for(int i=r;i>=l;--i)
#define pb push_back
#define mk make_pair
#define ll long long
#define u32 unsigned int
const int maxn=5e4+10;
int n,m;
vector<pair<int,char>>g[maxn];
//空格开头 1
//中间 2
//已经结尾 3
u32 dp[maxn][10];
signed main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
cin>>n>>m;
rep(i,1,m){
int a,b;
char tmp;
cin>>a>>b>>tmp;
g[a].push_back(mk(b,tmp));
}
rep(i,1,n){
for(int j=0;j<int(g[i].size());++j){
int to=g[i][j].first,sig=g[i][j].second;
if(sig<='z'&&sig>='a'){
dp[to][2]+=dp[i][2];//小写字母一定是中间态的转移
}
else if(sig=='_'){
dp[to][1]+=dp[i][1];//空格可以在任何一个位置,继承所有状态
dp[to][2]+=dp[i][2];
dp[to][3]+=dp[i][3];
//空格还可以单独成为一个合法路径,+1
dp[to][1]++;
}
else if(sig<='Z'&&sig>='A'){
dp[to][2]+=dp[i][1];//大写字符,出现在空格字符之后,状态为中间
//单独的大写字符也满足状态
dp[to][2]++;
}
else dp[to][3]+=dp[i][2];//结尾状态,为中间态+句号
}
}
u32 ans=0;
rep(i,1,n)ans+=dp[i][3];
cout<<ans<<endl;
return 0;
}