Solution
首先,能够比较明显的看出来,它的路径类型都是形如 (2^i) ,所以我们能够想到倍增。
然后我们观察数据范围,发现 (nleq 500) 。我去,那我乱搞不就行了
考虑倍增的DP,设状态 (dp[0/1][s][u][v]) 为从 (u) 到 (v) 存在一条以 (0/1) 开头,长度为 (2^s) 的路径,那么状态转移方程为 (dp[0/1][s][u][v]=dp[0/1][s-1][u][k]or dp[1/0][s-1][k][v]) ,其中 (k) 为中间转移点。
然后考虑如何计算答案,因为二进制的某个性质,当前位有 (1) 要比后面都是 (1) 还大,所以从高位向低位贪心,并用一个 (tmp) 来表示状态,及时更新。
细节:因为大于 (10^{18}) 就输出 (-1) ,所以开 (long~long) ,并在统计答案的时候及时判断。
代码
#include<cmath>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=510;
const ll INF=1e18;
int n,m,now;
ll ans,pow2[100];
bitset<N> f[2][70][N],g,tmp;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
int main(){
n=read();m=read();
for(int i=1,u,v,w;i<=m;i++){
u=read();v=read();w=read();
f[w][0][u][v]=1;
}
for(int i=1;i<=60;i++)
for(int u=1;u<=n;u++)
for(int v=1;v<=n;v++){
if(f[0][i-1][u][v]!=0) f[0][i][u]|=f[1][i-1][v];
if(f[1][i-1][u][v]!=0) f[1][i][u]|=f[0][i-1][v];
}
if(f[0][60][1].count()){
puts("-1");
return 0;
}
now=0;tmp[1]=1;pow2[0]=1;
for(int i=1;i<=60;i++) pow2[i]=pow2[i-1]*2;
for(int i=60;i>=0;i--){
g.reset();
for(int j=1;j<=n;j++)
if(tmp[j]) g|=f[now][i][j];
if(g.count()!=0){
now^=1;tmp=g;
ans+=pow2[i];
}
}
if(ans>INF) puts("-1");
else printf("%lld
",ans);
return 0;
}