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