【COCI2014】utrka

    xiaoxiao2021-03-25  121

    Description

    给定一个带权有向图,边权可能为负,输出边数最小的正环,并保证边数最小的情况下使环内边权和最大,输出最大边权和。 2<=N<=300,2<=M<=N*(N-1)

    Analysis

    并不知道SPFA能不能做,好像是水法 弄一个邻接矩阵 Ak[N][N] ,我们可以像矩阵乘法一样乘法若干次,求出某点走若干步到某点的最长路,那么 Ak 就表示乘了k次 于是,朴素想法可以乘一次判断一次,但是乘法为O(n^3)的,总复杂度为O(n^4) TLE 一次一次乘太慢了,我们可以采用倍增的思想 Ak 改成表示乘 2k 后的结果 一直乘,直到满足条件的前一个k 然后,可以以A的2的幂相乘得到最后的答案,k从大到小,倍增的思想只要不合法就乘 时间复杂度优化为 O(n3logn) 注意,本题需要一定卡常技巧

    Code

    #include<cstdio> #include<cstring> #include<algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) using namespace std; typedef long long ll; const int N=305; typedef ll mat[N][N]; int _2[N],n,m; mat a,b,c[N]; ll pd(mat a) { ll mx=0; fo(i,1,n) mx=max(mx,a[i][i]); return mx; } void mul(mat &c,mat a) { memset(c,0,sizeof(c)); } int main() { freopen("utrka.in","r",stdin); freopen("utrka.out","w",stdout); _2[0]=1; fo(i,1,20) _2[i]=_2[i-1]*2; int u,v,x,y; scanf("%d %d",&n,&m); memset(c,200,sizeof(c)); fo(i,1,m) { scanf("%d %d %d %d",&u,&v,&x,&y); c[0][u][v]=y-x; } int l=1; for(;;l++) { memcpy(c[l],c[l-1],sizeof(c[l-1])); fo(i,1,n) fo(j,1,n) fo(k,1,n) c[l][i][j]=max(c[l][i][j],c[l-1][i][k]+c[l-1][k][j]); if(pd(c[l])) break; } l--; memcpy(a,c[l],sizeof(c[l])); int ans=_2[l]; while(--l>=0) { memcpy(b,a,sizeof(a)); fo(i,1,n) fo(j,1,n) fo(k,1,n) b[i][j]=max(b[i][j],a[i][k]+c[l][k][j]); if(!pd(b)) { memcpy(a,b,sizeof(b)); ans+=_2[l]; } } printf("%d ",ans+1); memcpy(b,a,sizeof(a)); fo(i,1,n) fo(j,1,n) fo(k,1,n) b[i][j]=max(b[i][j],a[i][k]+c[0][k][j]); printf("%d",pd(b)); return 0; }
    转载请注明原文地址: https://ju.6miu.com/read-11165.html

    最新回复(0)