CF781D Axel and Marston in Bitland [倍增 矩阵乘法 bitset]
好开心第一次补$F$题虽然是$Div.2$
题意:
一个有向图,每条边是$0$或$1$,要求按如下规则构造一个序列然后走:
第一个是$0$,每次复制当前序列,取反后贴在后面
如:$0,01,0110,01101001,...$
注意走的是$01101001$而不是$0 01 0110 01101001$
一开始读错题了然后郁闷了好久....
求最多走几次,如果$> 1e18$就输出$-1$
(1 ≤ n ≤ 500, 0 ≤ m ≤ 2n^2)
看到了左下角的$matrices$标签就开始往矩阵乘法想
图上的路径问题可以转化成邻接矩阵的矩阵乘法
我们如果用矩阵乘法预处理出按照序列走了几次之后的矩阵貌似可做啊
然后想不太明白了就参考了一下别人的代码
$g[0/1][i]$表示正$0$反$1$,走了$2^i$步之后的矩阵
显然存在递推关系
$g[0][i]=g[0][i-1]*g[1][i-1];\ g[1][i]=g[1][i-1]*g[0][i-1];$
然后用倍增的思想从大到小选择看看能不能走这个矩阵就行了
用$a[i]$表示当前能否走到$i$,$a[i]==1$就可以走到当前矩阵所有$i$能走到的点啦
注意这里的矩阵$c[i][j]$表示能否从$i$走到$j$
然后可以用$bitset$优化,矩阵乘法可以省掉一层循环
不知道为什么常数超小......374ms
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <string> #include <bitset> using namespace std; typedef long long ll; const int N=502,M=61; const ll INF=1e18; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int n,m,u,v,p; struct Matrix{ bitset<N> a[N]; bitset<N>& operator [](int x){return a[x];} void ini(){for(int i=1;i<=n;i++) a[i].reset();} }g[2][M]; Matrix operator *(Matrix a,Matrix b){ Matrix re; for(int i=1;i<=n;i++) for(int k=1;k<=n;k++) if(a[i][k]) re[i]|=b[k]; return re; } bitset<N> a,t; ll ans; int main(){ //freopen("in","r",stdin); n=read();m=read(); for(int i=1;i<=m;i++){ u=read();v=read();p=read(); g[p][0][u][v]=1; } for(int i=1;i<M;i++){ g[0][i]=g[0][i-1]*g[1][i-1]; g[1][i]=g[1][i-1]*g[0][i-1]; } int s=0;a[1]=1; for(int now=M-1;now>=0;now--){ t.reset(); for(int i=1;i<=n;i++) if(a[i]==1) t|=g[s][now][i]; if(t.count()){ a=t; s^=1; ans+=(1LL<<now); } if(ans>INF) {puts("-1");return 0;} } cout<<ans; }
Copyright:http://www.cnblogs.com/candy99/