qbxt Day 4

Day 4

考试题解

T1 油箱

签到失败555。考试时一直卡在如何判定所有的点连成一个环,其实反向建个图就搞定了。其实我当时的思路是判定完联不联通然后跑\(Dijkstra\)去找最小环上的最大值,但是一直卡在判环上。谁知道没有-1的数据点,早知道我直接上\(Dijkstra\)\(A\)了。其实这也很好想,要是真正的考试,不可能让不会做的选手输出-1从而得到很多分,我觉得最多也就5分。所以我其实应该选择打上这个的,而不是打60跑路。(我其实在担心捆绑测试卡死我)

正解

二分答案,问题变成判断一个有向图能否任意两点联通。相当于判断点1是否能到达所有点并且所有点都能到达点1。可以建立正向图和反向图,并从点1开始dfs或bfs遍历。(反向建图操作确实骚)

也可以直接上\(tarjan\)但是我不会。

bfs+二分代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int N=50005,M=400005;
int n,m,tot1,tot2,maxx,ans;
int ver1[M],head1[N],edge1[M],Next1[M],d1[N],ver2[M],head2[N],edge2[M],Next2[M],d2[N];
bool v1[N],v2[N];
queue<int> q1,q2;
void add1(int x,int y,int z){
	ver1[++tot1]=y;
	edge1[tot1]=z;
	Next1[tot1]=head1[x];
	head1[x]=tot1;
}
void add2(int x,int y,int z){
	ver2[++tot2]=y;
	edge2[tot2]=z;
	Next2[tot2]=head2[x];
	head2[x]=tot2;
}
void bfs1(int sum){
	memset(v1,0,sizeof(v1));
	q1.push(1);
	v1[1]=1;
	while(!q1.empty()){
		int x=q1.front();
		q1.pop();
		for(int i=head1[x];i;i=Next1[i]){
			int y=ver1[i],z=edge1[i];
			if(v1[y]) continue;
			if(z<=sum){
				v1[y]=1;
				q1.push(y);
			}
		}
	}
}
void bfs2(int sum){
	memset(v2,0,sizeof(v2));
	q2.push(1);
	v2[1]=1;
	while(!q2.empty()){
		int x=q2.front();
		q2.pop();
		for(int i=head2[x];i;i=Next2[i]){
			int y=ver2[i],z=edge2[i];
			if(v2[y]) continue;
			if(z<=sum){
				v2[y]=1;
				q2.push(y);
			}
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1,x,y,z;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		add1(x,y,z);
		add2(y,x,z);
		maxx=max(maxx,z);
	} 
	bfs1(maxx);
	bfs2(maxx);
	for(int i=1;i<=n;i++){
		if(!v1[i]||!v2[i]){
			printf("-1\n");
			return 0;
		}	
	}
	int l=0,r=maxx;
	while(l<=r){
		int mid=(l+r)>>1;
		bfs1(mid);
		bfs2(mid);
		int t=0;
		for(int i=1;i<=n;i++){
			if(!v1[i]||!v2[i]){
				t=1;
				l=mid+1;
				break;
			}
		}
		if(!t){
			r=mid-1;
			ans=mid;
		}
	}
	printf("%d\n",ans);
	return 0;
}

T2 求和

20分做法:

直接暴力模拟:\(O(n^2*m)\)

40分做法:

前缀和优化。

100分做法:

推出玄学式子。(好像有点懵b)

序列修改的话:线段树。树的话:树链剖分。

T3 染色

动态规划

没听懂???????????????????

T4 数字

看成不断添加一个新数字,这样相当于好数组成的哈密顿通路(不重复的经过每一个点)。那么只需要求出两两好数之间最短距离,然后状压dp求出哈密顿通路。

把每一条边看作新加入一个数字,然后边权就是这个数

如果状态转移的时候出现坏数,那么划掉这个数

图论

最短路算法

floyd算法

多源最短路算法,可以计算一个图中任意两点最短路。实质是\(dp\)。时间复杂度:\(O(n^3)\)。空间复杂度:\(O(n^2)\)。用邻接矩阵存图,由于其\(dp\)本质不能处理负环。

可以用来求最小环。设\(k\)是最小环中编号最大的那一个点,那么从\(i\)\(j\)的最短路在这之前只能由小于\(k\)的编号的点中转得来,所以符合\(k\)是编号最大的点。求完之后再更新一遍最短路。

核心代码如下

for(int k=1;k<=n;k++){
		for(int i=1;i<k;i++){
			for(int j=i+1;j<k;j++){
				res=min(res,dis[i][j]+mp[i][k]+mp[k][j]);
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	} 

Dijkstra算法

采用贪心策略,因此无法处理负权边。堆优化之后时间复杂度变为\(O(mlogm)\)。但不优化的时间复杂度是\(O(n^2)\)。也就是在稠密图中不优化反而更高效。

SPFA

可以判断负环:如果某个点入队n次则说明存在负环。时间复杂度是\(O(mlohm)\),但在特殊构造的数据下可能会退化为\(O(mn)\)

总结:floyd、SPFA可以处理负边权,SPFA可以判负环,floyd可以判最小环

习题

是否在最短路上

最短路个数这个题类似,\(if(f_{i,j}==f_{i,k}+d_{k,j}+f_{k,j})\)

牛 poj3660

用floyd传递闭包判断两点是否联通。\(f_{i,j}=f_{i,k}&f_{k,j}\)\(f\)数组中式bool变量,用来判断两点是否联通。

floyd经典应用(经过\(k\)条边的最短路)bzoj1706

倍增floyd

牛:分层图 bzoj 1579

加油:分层图 poj 3635

每次只有两种操作,加一个油或者前进一步。

Tales

奇最短路和偶最短路(讲过)

逛公园

\(dp_{u,k}\)表示从点1走到点\(u\)比最短路长\(k\)的路径条数。先求一遍最短路,然后\(dp\)转移:\(dp_{u,k}\)=\(sum(v->u,w)\) $dp_{v,dis_u+k-w-dis_v_}。

但是如果边在最短路上则不太好判断状态转移顺序,则可以使用记忆化搜索,自动按照拓扑序。如果存在零环,那么则有无穷多条路径满足条件。

打怪

看到\(p\)<=13,知道要状压记状态。但是由于没有拓扑序,所以要用最短路算法Dijkstra求拓扑序。

次短路

多几个\(if\)判断即可(严格次小生成树中有类似的分类讨论)

差分约束

求解查分约束系统就是单源最短路或者最长路问题。对于形如\(x_i<=x_j+c\)的形式,建一条从\(j\)\(i\)权值为\(c\)的边,最终求\(x_t-x_s\)的最小值就是求单源最短路,即为最紧的限制。

例题 糖果

最小生成树

Kruscal:贪心,时间复杂度\(O(mlogm)\)。适用于稀疏图。

魔术师

考虑前缀和\(sum\),区间\([l,r]\)中个数奇偶性相当于\(sum_l-1\)\(sum_r\)奇偶性是否相同。相当于在\(l-1\)\(r\)之间连一条边,如果知道连通块中一个点的值,那么通过奇偶性的变化就可以推得哪一个杯子里有球,那么求出\(sum_0\)\(sum_n\)的最小生成树就是最终的答案。

比如说\((sum2-sum1)%2=1\)\((sum3-sum1)%2=0\)。那么\(sum3\)底下一定有球。每次询问相当于加一条边,而只有每一个块都联通之后,才能知道每一个杯子底下到底有没有球。

货车运输

求出最大瓶颈生成树,然后所求即为生成树上一条路径中的最小边权。

通过倍增解决。

树上倍增

可以求\(LCA\)链和,链上最大最小值。

Tarjan求LCA

没听懂,时间复杂度是\(O(n)\)

严格次小生成树

每加入一条边

维护一种数据结构:支持更改一个点的点权,求一个子树的最小点权,换根。

拓扑问题

如果点\(a\)可以到达点\(b\),那么在拓扑序中点\(a\)在点\(b\)前面。可以按照拓扑顺序来\(dp\)。这样就可以避免需要某一个点的值转移得来,但这个点还没有求过。

求拓扑排序:用一个队列来维护,先将所有入度为0的点加入队列。每次弹出队首,然后删掉它的所有出度。如果出现入度为0的点,那么入队。直到队列为空,就得到了拓扑序。

拓扑排序判环:在图上运行拓扑排序算法,(但一般是dfs判环)

用优先队列维护下标,每次拿出下标最小的入度为0的节点,这样就可以得到字典序最小的拓扑序。

求一个拓扑图从一个点出发的路径条数

\(f_i\)表示从\(i\)点出发的路径条数,\(f_i\)=sigma f_j+1\(,其中\)i\(到\)j\(有一条边。用拓扑序的逆序求\)f$值。

求一个拓扑图中从S点到T点的必经边。

求出\(S\)到每个点的路径条数\(f_i\),以及每个点到\(T\)的路径条数\(g_i\)。如果一条x到y的边满足\(f_x*g_y\)=
那么为必经边。

SDOI 2009 Elaxia的路线

??????????????

dilsorth定理

设拓扑序中可以用\(n\)个链来覆盖所有的点,那么\(n\)=最长反链长度。

导弹拦截:第二问相当于每个导弹向不高于自己的导弹连边,求最少链覆盖所有边。根据定理,相当于最长反链,即最长上升子序列。

垃圾

只能往右或往下,相当于只能从左边或上边转移而来。连边遍历拓扑序。相当于求一个最小链覆盖,从右下角往左上角走,求出最长反链的长度即可。

?无题

建一个源点\(S\)和汇点\(T\),分别向所有点连边,

??????????????????????

二分图

一个无向图可以被分为两个点集,且点集内部没有连边。等价于图中没有奇环。

\(dfs\)对整个二分图染色

关押罪犯

二分答案+dfs染色

匈牙利算法(二分图最大匹配)

?????????(得学)

棋盘

?????????????????

二分图的最小覆盖=最大匹配

求最大独立集=总点数-最大匹配

机器调度

还没看完题就讲完了????

棋盘2

\(n*n\)棋盘上可以放的最多的不会相互攻击的骑士数量。求最大独立集。

posted @ 2020-10-04 23:58  徐明拯  阅读(182)  评论(1编辑  收藏  举报