24暑假集训day5上午
图论
差分约束
有
给定一些形如
例如
在单源最短路问题中,如果存在一条
所以对于每个
如果图中存在负环,则无解。假如负环节点编号为
如果图中没有负环,则有解。跑完最短路后,令
如果有
图不连通时,各个连通块可以独立考虑。
差分约束求值
注意到我们可以给每个
但是可以“求出一组合法解,使得
由于
由于
如果有
思路:
设
农场
农场
农场
使用 SPFA 判断图中是否有负环。有负环则无解,无负环则有解。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#include<set>
#include<unordered_map>
#include<bitset>
#include<climits>
#include<cassert>
using namespace std;
const int MAXN=100005;
int n,m,s,dis[MAXN];
bool inqueue[MAXN];
queue<int> Q;
int flag[MAXN];
vector<pair<int,int> > edges[MAXN];
int main(){
int n,m;
cin >> n >> m ;
memset(dis,0x3f,sizeof(dis));
for(int i=0,u,v,w;i<m;i++){
int op,a,b,c;
cin>>op;
if(op==1){
cin >> a >> b >> c;
edges[a].emplace_back(b,-c);
}else if(op==2){
cin >> a >> b >> c;
edges[b].emplace_back(a,c);
}else{
cin >> a >> b;
edges[a].emplace_back(b,0);
edges[b].emplace_back(a,0);
}
}
for(int i=1;i<=n;i++){
edges[0].emplace_back(i,0);
}
dis[s]=0;
Q.push(s);
inqueue[s]=true;
while(!Q.empty()){
int x=Q.front();
Q.pop();
inqueue[x]=false;
for(auto edge:edges[x]){
if(dis[edge.first]<=dis[x]+edge.second)continue;
dis[edge.first]=dis[x]+edge.second;
if(!inqueue[edge.first]){
Q.push(edge.first);
flag[edge.first]=flag[x]+1;
if(flag[edge.first]>n){
cout<<"No\n";
return 0;
}
inqueue[edge.first]=true;
}
}
}
cout<<"Yes\n";
return 0;
}
拓扑排序
对于一个有向无环图,为每个节点
在有环图上无法做到,假设环为
最开始的节点一定不能有入度。所以我们可以每次任选一个没有入度的节点,将其拓扑序置为
由于删完节点后,图仍然是 DAG,所以存在合法拓扑序。
换句话说,任选一个没有入度的节点都是合法的。
用一个队列维护所有入度
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#include<set>
#include<unordered_map>
#include<bitset>
#include<climits>
#include<cassert>
#define int long long
using namespace std;
const int N=100005;
inline int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
int ord[N], inDegree[N];
int n, m;
queue<int> Q;
vector<int> nextPoints[N];
signed main(){
cin >> n >> m;
for(int i=0,u, v; i< m; i++){
cin >> u >> v;
nextPoints[u].push_back(v);
inDegree[v]++;
}
for(int i=0;i<n; i++){
if(inDegree[i]==0){
Q.push(i);
}
}
int cnt = 0;
while(!Q.empty()){
int x=Q.front();
Q.pop();
ord[x] = ++cnt;
for(auto y: nextPoints[x]){
inDegree[y]--;
if(inDegree[y]==0){
Q.push(y);
}
}
}
return 0;
}
如果原图中存在环,那么这个环以及其能到达的所有点,都不会被删除。
所以可以使用拓扑排序判断有向图是否是 DAG。只需要在结束时判断
两个相似的问题
要求拓扑序靠前的编号尽量小。即最小化
要求编号小的拓扑序尽量靠前。即最小化
其中
拓扑序靠前的编号尽量小
普通的拓扑排序每次任选一个 inDegree=0
的节点。现在我们只需要每次取编号最小的节点即可。
用优先队列替换队列维护 inDegree=0
的节点,每次取出编号最小的节点。
编号小的拓扑序尽量靠前
直接做是不可行的,我们不知道删掉哪个点能最快到达
但是我们可以让“编号小的拓扑序尽量靠后”:尽量拖延删除 1 号点的时间,直到不得已再删(此时队列中只有
然后反向建图即可。反向的拓扑序靠后就是正向的拓扑序靠前。
拓扑序计数
给定一张有向无环图,求其合法拓扑序个数。
设
转移时,枚举
其中 ind_𝑖=0
表示
次小生成树问题
求次小生成树。(可能与最小生成树边权和相等)
第三小?
经过 1 号点的最小环
给定一个有向图,无重边无自环,求经过
边权非负,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!