Topological sorting
//topological sorting
#include<iostream>
using namespace std;
const int N = 5005;
const int M = 500005;
const int mod = 80112002;
int n,m,a,b,en = 0;
struct Edge{
int next,to;
}edge[M];
int head[N] = {0};
int in[N] = {0},out[N] = {0};
int queue[N] = {0};
int front = 0,back = 0;
bool in_queue[N] = {0};
int f[N] = {0};
long long ans = 0;
void add(int u,int v){
edge[++en].to = v;
edge[en].next = head[u];
head[u] = en;
in[v] ++;
out[u] ++;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);//被吃和吃
add(a,b);
}
for(int i=1;i<=n;i++){//queue 1base
if(in[i]==0) {
queue[++back] = i;
f[i] = 1;
}
}
while(back>front){//back==front是空,front没存
int u = queue[++front];
for(int p=head[u];p;p=edge[p].next){
int v = edge[p].to ;
f[v] = (f[v]+f[u])%mod;
if(in_queue[v]) continue;
in[v]--;
if(in[v]==0){
in_queue[v] = true;
queue[++back] = v;
}
}
}
for(int i=1;i<=n;i++){
if(out[i]==0) ans = (ans+f[i])%mod;
}
printf("%d\n",ans);
}
最小生成树Prim
//MSP 无向图
#include<iostream>
using namespace std;
const int N = 5e3+5;
const int M = 2e5+5;
const int INF = 0x3f3f3f3f;
int n,m,en = 0,x,y,z;
struct Edge{
int next,to;
int val;
}edge[M*2];
int head[N] = {0};
int d[N] = {0};
bool in[N] = {0};
long long ans = 0;
void add(int u,int v,int w){
edge[++en].to = v;
edge[en].val = w;
edge[en].next = head[u];
head[u] = en;
}
void insert(int u){
in[u] = true;
ans += d[u];
for(int p=head[u];p;p=edge[p].next){
int v = edge[p].to ;
if(!in[v]) d[v] = min(d[v],edge[p].val);//呃呃这里不加d[u]的
}
}
int getmin(){
int minx = 0;
for(int i=2;i<=n;i++){
if(in[i]) continue;
if(!minx) minx = i;
if(d[minx]>d[i]) minx = i;
}
return minx;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++){
d[i] = INF;
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
insert(1);
for(int i=2;i<=n;i++){
int tmp = getmin();
if(d[tmp]==INF){
printf("orz\n");
return 0;
}
insert(tmp);
}
printf("%d\n",ans);
return 0;
}
可以sort就用kruskal,不可以就prim
最小生成树Kruskal
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int fa[5005],n,m,ans,efrom,eto,cnt;
struct Edge{
int from,to,w;
}edge[200005];
inline bool cmp(Edge a,Edge b){
return a.w<b.w;
}
//快排的依据(按边权排序)
inline int find(int x){
while(x!=fa[x]) x=fa[x]=fa[fa[x]];
return x;
}
inline void kruskal(){
sort(edge,edge+m,cmp);
//将边的权值排序
for(register int i=0;i<m;i++){
efrom=find(edge[i].from), eto=find(edge[i].to);//并查集判断环
if(efrom==eto){
continue;
}
//若出现两个点已经联通了,则说明这一条边不需要了
ans+=edge[i].w;
//将此边权计入答案
fa[eto]=efrom;
//将efrom、eto合并
if(++cnt==n-1){
break;
}
//循环结束条件,即边数为点数减一时
}
}
int main(){
scanf("%d%d",&n,&m);
for(register int i=1;i<=n;i++){
fa[i]=i;
}
//初始化并查集
for(register int i=0;i<m;i++){
cin>>edge[i].from>>edge[i].to>>edge[i].w;
}
kruskal();
printf("%d",ans);
return 0;
}
inline bool cmp要会写,然后就是这里并查集只有路径压缩没有按秩合并,加上也成……
xs,偶尔考试可以用sort,写个cmp就很好办
Kruskal的其他用法:生成完全图
意思就是你生成的图的最小生成树要是给定的那个树。
思路是对树进行kruskal,每次先访问最小的边,然后进行一些集合的合并,(其实每次访问时两个点一定不是在同一个集合,但是为了计数–集合的大小,我们还是写一个并查集比较方便),合并的时候顺带连上两个集合之间的所有边(除了树边以外都是树边长+1的长度)。
AC代码如下:
#include<iostream>
using namespace std;
const int N = 1e5+5;
int n;
long long ans = 0;
struct Edge{
int from,to;//不是前向星!!
int val;
Edge &operator =(Edge x){
from = x.from;
to = x.to;
val = x.val;
return *this;
}
bool operator >=(Edge x){
return val >= x.val;
}
}edge[N];//kruskal,只存单向
int fa[N] = {0},siz[N] = {0},dep[N] = {0};
int divide(int low,int high,Edge a[]){
Edge k = a[low];
while(low!=high){
while(low<high && a[high]>=k) high--;
if(low<high){
a[low] = a[high];
low++;
}
while(low<high && k>=a[low]) low++;
if(low<high){
a[high] = a[low];
high--;
}
}
a[low] = k;
return low;
}
void quicksort(int low,int high){
if(low>=high) return ;
int mid = divide(low,high,edge);
quicksort(low,mid-1);
quicksort(mid+1,high);
return ;
}
int find(int u){
if(fa[u]==u) return u;
return fa[u] = find(fa[u]);
}
void kruskal(){
for(int i=1;i<n;i++){
int u = edge[i].from ,v = edge[i].to ,w = edge[i].val ;
if(find(u)==find(v)) continue;
ans += w;
ans += (w+1)*((long long)siz[fa[v]]*siz[fa[u]]-1);
int fu = fa[u],fv = fa[v];//知道写fu fv的好处在哪吗,三个if语句互不干扰,只进去一个,
//如果写fa[u] fa[v]会改变if括号里面要用到的值,会导致出问题
if(dep[fu]<dep[fv]){
siz[fv] += siz[fu]; fa[fu] = fv;
}
if(dep[fu]>dep[fv]){
siz[fu] += siz[fv]; fa[fv] = fu;
}
if(dep[fu]==dep[fv]){
siz[fv] += siz[fu]; fa[fu] = fv; dep[fv]++;
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
fa[i] = i;
siz[i] = 1;
}
for(int i=1;i<n;i++){
scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].val);
}
quicksort(1,n-1);//必须写1~n-1,不能写1~n因为第n条边是0呀
kruskal();
printf("%lld\n",ans);
return 0;
}
注意:
- kruskal存的时候不必存前向星,因为根本用不上,存个双向反而给排序带来了困惑。
- 排序不能写
bubble
,会超时,用quicksort
会快一些,但是注意uicksort
写的时候传参传1~n-1
,不要多传第n
条边进去,根本就没有第n
条边 - 按秩合并加了没加一样的
- 注意中间计算过程也要加
(long long)
,否则会中途爆掉然后WA
- 笑死了,quicksort还是不会写,复制的之前写的luogu40分代码(因为没用随机数)