2024.7.27清北学堂集训day5

2024.7.27清北学堂集训day5

就算你一道题不会,把你该拿的分拿到,不影响你拿高分 ——zhx真理

T1

这一道题号是很简单的,不一定算图论的部分,dfs判断一下图是否连通,因为如果不联通就没有最小生成树,最后排序输出前 \(n-1\) 条边的和

点击查看代码
#include <bits/stdc++.h>

#define int long long

using namespace std;

int n,m;
int u,v;
int d[450010];

struct edge {
	int to;
	int w;
};

vector<edge> e[100010];
bool vis[100010];

void dfs(int x) {
	for(auto y:e[x]) {
		if(!vis[y.to]) {
			vis[y.to]=1;
			dfs(y.to);
		}

	}
}

signed main() {
	cin>>n>>m;
	for(int i=1; i<=m; ++i) {
		cin>>u>>v>>d[i];
		e[u].push_back((edge) {
			v,d[i]
		});
		e[v].push_back((edge) {
			u,d[i]
		});
	}
	sort(d+1,d+m+1);
	vis[1]=1;
	dfs(1);
	//cout<<1<<"\n";
	for(int i=1; i<=n; ++i) {
		if(vis[i]==0) {
			cout<<-1<<"\n";
			return 0;
		}
	}
	int sum=0;
	for(int i=1; i<=n-1; ++i) {
		sum+=d[i];
	}
	cout<<sum<<"\n";
}

T2

这一个题首先想到的是60分的部分分,然后优化。
百分之百的做法是怎样的?因为我们求的是连通块个数,所以进行相邻的连边即可

image

但是时间复杂度依然是 \(O(nm)\) 的,该怎么办?

image

每次跳到根,每条边就只会被连接一次

赛时加上了一个特判,成功从60pts------->0pts(。。

T3

很标准的图论题目,进行dijkstra/spfa,跑一遍最短路,记录 \(f_i,g_i\),代表走到i的方案数,走到i的最短路之和进行转移

注意,减法比取模快大概1000倍。注意加法时用,乘法有可能远远大于模数

进行最短路一定不要取模!!!一定!!!

为什么?因为如果一个边权为10000,模数100,边权就成为了0。。从最长路变成了最短路

点击查看代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<algorithm>

using namespace std;

const int maxn=100010;
const int maxm=200010;
const int mo=1000000007;

#define inc(a,b) {a+=b;if (a>=mo) a-=mo;}

int n,m,en,dist[maxn],a[maxn],f[maxn],g[maxn];

bool inq[maxn];

queue<int> q;

struct edge {
	int e,d;
	edge *next;
}*v[maxn],ed[maxm];

void add_edge(int s,int e,int d) {
	en++;
	ed[en].next=v[s];
	v[s]=ed+en;
	v[s]->e=e;
	v[s]->d=d;
}

bool cmp(int a,int b) {
	return dist[a]<dist[b];
}

int main() {
	scanf("%d%d",&n,&m);
	for (int i=1; i<=m; i++) {
		int s,e,d;
		scanf("%d%d%d",&s,&e,&d);
		add_edge(s,e,d);
	}
	memset(dist,0x3f,sizeof(dist));
	dist[1]=0;
	q.push(1);
	inq[1]=true;
	while (q.size()) {
		int now=q.front();
		q.pop();
		inq[now]=false;
		for (edge *e=v[now]; e; e=e->next) {
			int newd=dist[now]+e->d;
			if (newd<dist[e->e]) {
				dist[e->e]=newd;
				if (!inq[e->e]) q.push(e->e),inq[e->e]=true;
			}
		}
	}
	for (int i=1; i<=n; i++)
		a[i]=i;
	sort(a+1,a+n+1,cmp);
	f[1]=1;
	g[1]=0;
	int ans1=0,ans2=0;
	for (int i=1; i<=n; i++) {
		int now=a[i];
		if (dist[now]==dist[0]) break;
		inc(ans1,dist[now]);
		inc(ans2,g[now]);
		for (edge *e=v[now]; e; e=e->next)
			if (dist[e->e]==dist[now]+e->d) {
				inc(f[e->e],f[now]);
				inc(g[e->e],f[now]);
				inc(g[e->e],g[now]);
			}
	}
	printf("%d %d\n",ans1,ans2);

	return 0;
}

T4

加上特殊性质,分数+20pts

+亿点点运气+20pts

+一点点实力+40pts(爆搜,考试必备)

image

二分图匹配即可--------->匈牙利算法

匈牙利算法

就是为了解决上方二分图匹配的算法的

对于每一个左边的去匹配右边的

点击查看代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=110;
const int maxm=510;

int n,m,en,f[maxn],fd[maxn],fid[maxn],dep[maxn],result[maxm],match[maxn][maxm],use[maxm],s[maxm],e[maxm],d[maxm];

struct edge {
	int s,e,d,id;
	edge *next;
}*v[maxn],ed[maxn<<1];

void add_edge(int s,int e,int d,int id) {
	en++;
	ed[en].next=v[s];
	v[s]=ed+en;
	v[s]->e=e;
	v[s]->d=d;
	v[s]->id=id;
}

void dfs(int now,int fa) {
	dep[now] = dep[fa]+1;
	for (edge *e=v[now]; e; e=e->next)
		if (e->e != fa) {
			f[e->e] = now;
			fd[e->e] = e->d;
			fid[e->e] = e->id;
			dfs(e->e,now);
		}
}

bool dfs(int l) {
	for (int r=1; r<=m; r++)
		if (!use[r] && match[l][r]) {
			use[r]=true;
			if (!result[r] || dfs(result[r])) {
				result[r]=l;
				return true;
			}
		}
	return false;
}

int go(int p) {
	if (p==f[p]) return p;
	else return f[p]=go(f[p]);
}

int work(int r) {
	en=0;
	memset(v,0,sizeof(v));
	memset(f,0,sizeof(f));
	for (int i=1; i<=n; i++)
		f[i]=i;
	for (int i=1; i<=n; i++)
		if (r!=i) {
			if (go(s[i])==go(e[i])) return 0x3f3f3f3f;
			f[go(s[i])]=go(e[i]);
			add_edge(s[i],e[i],d[i],i);
			add_edge(e[i],s[i],d[i],i);
		}
	memset(dep,0,sizeof(dep));
	memset(fd,0,sizeof(fd));
	memset(match,0,sizeof(match));
	memset(f,0,sizeof(f));
	memset(result,0,sizeof(result));
	dfs(1,0);
	for (int i=n+1; i<=m; i++)
		if (i==r || i>n) {
			int p1=s[i];
			int p2=e[i];
			int d=::d[i];
			while (p1!=p2) {
				if (dep[p1]<dep[p2]) swap(p1,p2);
				if (d < fd[p1]) match[fid[p1]][i]=true;
				p1=f[p1];
			}
		}

	int ans=0;
	for (int i=1; i<=n; i++) {
		memset(use,false,sizeof(use));
		if (dfs(i)) ans++;
	}
	return ans;
}

int main() {
	scanf("%d%d",&n,&m);
	for (int i=1; i<=m; i++)
		scanf("%d%d%d",&s[i],&e[i],&d[i]);
	int ans=m+1;
	for (int i=1; i<=n; i++)
		ans=min(ans,work(i));
	printf("%d\n",ans);

	return 0;
}
posted @ 2024-07-27 14:07  guoguo160  阅读(40)  评论(0)    收藏  举报