正环,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
#include
#include
#include
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<

 

你可能感兴趣的:(正环,noi.ac模拟赛,floyd倍增)