1. 程式人生 > >正環,noi.ac模擬賽,floyd倍增

正環,noi.ac模擬賽,floyd倍增

正題

       題目連結

       題意很顯然,求一個最小正環。

       如果f_{i,j,k}表示i到j走k步需要多久,那麼我們要找出最小的一個k使得有一個f_{i,i,k}不為0.

       很明顯可以floyd,每次乘一個走一步的矩陣,那麼乘到有正環就是答案。

       時間複雜度O(n^4)

       很高興。

       那麼明顯我們不會那麼傻,倍增就好了嘛,我們依次處理出走1,2,4,8步的答案,然後將它們類同倍增一樣乘起來,就是答案,有點像lca。

       注意,陣列初始化一開始覺得很蒙,應該是f[i][j]=(i==j?0:-1e9)

       

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

int n,m;
struct node{
	int g[310][310];
	friend node operator*(const node x,const node y){
		node q;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				q.g[i][j]=i==j?0:-1e9;
		for(int k=1;k<=n;k++)
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
					q.g[i][j]=max(q.g[i][j],x.g[i][k]+y.g[k][j]);
		return q;
	}
	bool pos()const{
		for(int i=1;i<=n;i++)
			if(g[i][i]>0) return true;
		return false;
	}
}f[10];

int main(){
	scanf("%d %d",&n,&m);
	int x,y,a,b;
	int now=-1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			f[0].g[i][j]=i==j?0:-1e9;
	for(int i=1;i<=m;i++){
		scanf("%d %d %d %d",&x,&y,&a,&b);
		f[0].g[x][y]=a;
		f[0].g[y][x]=b;
	}
	for(int i=1;i<=9;i++){
		f[i]=f[i-1]*f[i-1];
		if(f[i].pos()) {
			now=i;
			break;
		}
		
	}
	if(now==-1){
		printf("0");
		return 0;
	}
	int ans=0;
	node q;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			q.g[i][j]=i==j?0:-1e9;
	for(int i=now-1;i>=0;i--){
		if((q*f[i]).pos()) continue;
		q=q*f[i];
		ans+=(1<<i);
	}
	printf("%d\n",ans+1);
}