经过如下的方法生成一个序列:
(1)初始时只有一个 "P"。
(2)将当前字符串 s 的 "P" 变成 "B","B" 变成 "P" 获得 s',将 s' 接在 s 以后获得新的序列。
生成的前几个步骤获得字符串为 P,PB,PBBP,PBBPBPPB......优化
给定一个有向图,每条边上有字符 'P' 或者 'B'。求从点 1 出发走出如上序列的最长路径(即第一步走 s[1], 第二步走 s[2], ...)的最长可能长度。
若是长度 > 10^18,则输出 -1。ui
Input
第一行两个整数 n, m (1 ≤ n ≤ 500, 0 ≤ m ≤ 2n^2),表示点数与边数。
接下来 m 行,第 i 行三个整数 vi, ui, ti (1 ≤ vi, ui ≤ n, 0 ≤ ti ≤ 1), 表示一条 vi -> ui 的有向边。0 表示 'P',1 表示 'B'。保证无重边,但可能有自环。
Output
若是最长可能长度 > 10^18,输出 -1;不然输出最长可能长度。spa
对于前 2^i 个字符,咱们能够看做先走前 2^(i-1) 个字符,再走后 2^(i-1) 个字符(废话)。code
同时维护两个东西,一个是题目所说的 PBBPBPPB... 类型的路径 s1,另外一个是取反事后的 BPPBPBBP... 类型的路径 s2。
从 2^(i-1) 转移到 2^i,有 s1' = s1 + s2,s2' = s2 + s1。ip
记 f[0/1][i][x][y],0/1 表示是第一类仍是第二类,从 x 出发走 2^i 步是否能够到达 y。
转移时枚举中转点,从 f[0][i-1][x][z] 与 f[1][i-1][z][y] 转移到 f[0][i][x][y],从 f[1][i-1][x][z] 与 f[0][i-1][z][y] 转移到 f[1][i][x][y]。
最后求出 i = 0...60 对应的 f,利用倍增的思想获得最长可能长度(这里的过程相似于倍增求树上 lca 的过程)。
这个复杂度为 O(n^3*logA),其中 A = 10^18。字符串
注意到这个其实就像一个 Floyd 传递闭包,而众所周知这个东西是能够用 bitset 优化的,并且效果还很明显。
那么使用 bitset 事后,复杂度被优化成 O(n^3*logA / w),其中 w = 32 或 64。it
#include <cstdio> #include <bitset> using namespace std; bitset<500>bts[2][61][500], nw, tmp; int main() { int n, m; scanf("%d%d", &n, &m); for(int i=1;i<=m;i++) { int u, v, p; scanf("%d%d%d", &u, &v, &p), u--, v--; bts[p][0][u].set(v, 1); } for(int p=1;p<=60;p++) { for(int k=0;k<n;k++) for(int i=0;i<n;i++) { if( bts[0][p-1][i][k] ) bts[0][p][i] |= bts[1][p-1][k]; if( bts[1][p-1][i][k] ) bts[1][p][i] |= bts[0][p-1][k]; } } int type = 0; nw.set(0, 1); long long ans = 0; for(int i=60;i>=0;i--) { tmp = 0; for(int j=0;j<n;j++) if( nw[j] && bts[type][i][j].any() ) tmp |= bts[type][i][j]; if( tmp.any() ) { ans |= (1LL<<i); type ^= 1, nw = tmp; } } if( ans > (long long)(1E18) ) puts("-1"); else printf("%lld\n", ans); }
bitset 真好用.jpg。io