bzoj 2165: 大楼【Floyd+矩阵乘法+倍增+贪心】

1<<i的结果需要是long long的话i是long long是没用的……要写成1ll<<i……我别是个傻子吧
虽然写的是二进制贪心,但是我觉得二分可能更好写吧(但是会慢)
首先把矩阵乘法转换成Floyd的形式,注意是进行一次更新,也就是另开一个数组使得更新之后每个[i][j]都变成经过一或两段路,(i,j)之间的最长路
然后就可以倍增了,一直相乘就会变成经过一二四六八…段路,(i,j)之间的最长路,这里把倍增过程记下来
直到某次相乘之后符合要求(也就是[1][x]的最长路大于等于m)
然后按照二进制位从大到小贪心,扫倍增过程能乘就乘,然后再答案上加上1ll<<i
最后答案要+1

#include<iostream>
#include<cstdio>
using namespace std;
const int N=105;
const long long inf=1e18;
long long T,n,m;
struct qwe
{
    long long a[N][N];
    qwe operator * (const qwe &b) const
    {
        qwe c;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                c.a[i][j]=-inf;
        for(int i=1;i<=n;i++)
            c.a[i][i]=0;
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    c.a[i][j]=max(c.a[i][j],a[i][k]+b.a[k][j]);
        return c;
    }
}f[N];
long long read()
{
    long long r=0,f=1;
    char p=getchar();
    while(p>'9'||p<'0')
    {
        if(p=='-')
            f=1;
        p=getchar();
    }
    while(p>='0'&&p<='9')
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
bool ok(qwe a)
{
    for(int i=1;i<=n;i++)
        if(a.a[1][i]>=m)
            return 1;
    return 0;
}
int main()
{
    T=read();
    while(T--)
    {
        n=read(),m=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                f[0].a[i][j]=read();
                if(f[0].a[i][j]==0)
                    f[0].a[i][j]=-inf;
            }
        long long cnt=0,ans=1;
        while(1)
        {
            f[cnt+1]=f[cnt]*f[cnt];
            if(ok(f[++cnt]))
                break;
        }
        qwe p=f[0];
        for(int i=cnt;i>=0;i--)
        {
            qwe nw=p*f[i];
            if(!ok(nw))
            {
                ans+=1ll<<i;
                p=nw;
            }
        }
        printf("%lld\n",ans+1);
    }
    return 0;
}
posted @ 2018-07-01 09:41  lokiii  阅读(229)  评论(0编辑  收藏  举报