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
");
			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
",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)棋盘上可以放的最多的不会相互攻击的骑士数量。求最大独立集。

原文地址:https://www.cnblogs.com/57xmz/p/13768978.html