CF780F Axel and Marston in Bitland

Solution

首先,能够比较明显的看出来,它的路径类型都是形如 \(2^i\) ,所以我们能够想到倍增。

然后我们观察数据范围,发现 \(n\leq 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\n",ans);
    return 0;
}
posted @ 2020-10-09 08:19  jasony_sam  阅读(150)  评论(0编辑  收藏  举报