8.31 图论专项三模拟赛小记

A.虫洞,原题:bzoj 4773.负环

题意:给你一张有向图,找出点数最小的负环。

若能从题目中提取出这些信息会好做一些。

有负边权且 n 不大,适合用 floyd。需要跑的边巨多所以用倍增。

严格意义上来说我是在这道题才学会用倍增 + 矩乘优化 dijkstra or floyd 的。所以还是说一下当时的感受:

上学期初刚学倍增时一直不理解,那时对有关二进制的优化的体会非常有限。在这几天的练习中终于比较理解其思路了。在这里的倍增方式可以类比找 LCA 的思路:因为二次幂之和总能拼出任意一个数,所以从大到小枚举 2 的第 i 次幂,若跳到 2^i 后问题可以解决,则不跳(往下细找);反之则跳过去,因为要把答案拼出来。

综上,代码没什么好解释的了。但是这种倍增优化的思想是很重要的。

Code bzoj 4773
#include<bits/stdc++.h>
using namespace std;
const int N = 310;
const int inf = 0x3f3f3f3f;
int n, m;
int ans = 1;
struct M{
	int a[N][N];
	M()
	{
		for(int i = 1; i < N; i ++ )
			for(int j = 1; j < N; j ++ ) a[i][j] = inf;
	}
	M operator *(const M &x) const{
		M b;
		for(int k = 1; k <= n; k ++ )
			for(int i = 1; i <= n; i ++ )
				for(int j = 1; j <= n; j ++ )
					b.a[i][j] = min(b.a[i][j], a[i][k] + x.a[k][j]); 
		return b;
	}
}f[33], x, y;
int main()
{
	scanf("%d%d", &n, &m);
	while(m -- )
	{
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		f[0].a[u][v] = w;
	}
	for(int i = 1; i <= n; i ++ ) f[0].a[i][i] = y.a[i][i] = 0;
    int t = 0;
    for(int i = 1; (1 << i) <= n; i ++ )
	{
		t = i;
		f[i] = f[i - 1] * f[i - 1];
	}
    for(int i = t; i >= 0; i -- )
	{
        x = y * f[i];
        int flag = 1;
        for(int j = 1; j <= n; j ++ ) if(x.a[j][j] < 0) {flag = 0; break;}
        if(flag) ans += (1 << i), y = x;
    }
    printf("%d", (ans > n) ? 0 : ans);
    return 0;
}

B.重要度,原题:P2047 [NOI2007] 社交网络

tip: syoj 上的答案是洛谷上的一半。


C.灾后重建,原题:P1772 [ZJOI2006] 物流运输


D.天天爱跑步

posted @ 2023-09-04 15:59  Moyyer_suiy  阅读(23)  评论(0编辑  收藏  举报