备战noip week2

苗条的生成树(Slim Span, ACM/ ICPC Japan 2007, UVa1395)
题意:给出一个图,求它的生成树使得其最大边与最小边的差最小
题解及证明:先对所有边按照权值排序。以其中每条边为起始边跑最小生成树
这样相当于在枚举生成树的最小边,由于最小生成树也是最小瓶颈生成树,因此此时最大边也是最小的

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,m,tot,fa[N];
struct edge{int fr,to,w;}e[N*N];
inline int read()
{
	int s=0,w=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
	return s*w;
}
inline void add(int x,int y,int s)
{
	e[++tot].fr=x;e[tot].to=y;e[tot].w=s;
}
inline bool cmp(const edge x,const edge y){return x.w<y.w;}
int fd(int x){return fa[x]==x?x:fa[x]=fd(fa[x]);}
inline int solve()
{
	int ans=1e9;
	sort(e+1,e+tot+1,cmp);
	for(int i=1;i<=tot;++i)
	{
		for(int j=1;j<=n;++j)fa[j]=j;
		int cnt=0;bool flag=0;
		for(int k=i;k<=tot;++k)
		{
			int f_a=fd(e[k].fr),f_b=fd(e[k].to);
			if(f_a==f_b)continue;
			++cnt;fa[f_a]=f_b;
			if(cnt==n-1){flag=1;ans=min(e[k].w-e[i].w,ans);break;}
		}
		if(!flag)break;
	}
	if(ans<1e9)return ans;
	return -1;
}
int main()
{
	while(7)
	{
		n=read(),m=read();
		if(!n&&!m)break;
		tot=0;
		for(int i=1;i<=m;++i)
		{
			int u=read(),v=read(),val=read();
			add(u,v,val);
		}
		printf("%d\n",solve());
	}
	return 0;
}

例题11-3 买还是建(Buy or Build, ACM/ ICPC SWERC 2005, UVa1151)
题意:平面上若干城市,联通两个城市的代价定义为欧几里得距离的平方。现有q(q<=8)个套餐,每个套餐需要价值v,且其中包含若干城市,表示花费v的代价可以使这些城市全部联通。求最小代价
题解:先忽略套餐,做一遍最小生成树。而后2^q枚举套餐选择情况,每次都在先前最小生成树的n-1条边中再做MST,不断更新答案即可
证明:由MST的环路性质可知如果开始时某条边不在MST中,那么它以后永远都不会在MST中

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int tot,t,n,q,val[N],fa[N];
int ans,x[N],y[N];
struct edge{int fr,to,w;}e[N*N];
vector<int>z[10];
inline int dis(int a,int b)
{
	return (x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]);
}
inline void pre()
{
	for(int i=1;i<=n;++i)
		for(int j=i+1;j<=n;++j)
			e[++tot].fr=i,e[tot].to=j,
			e[tot].w=dis(i,j);
}
inline bool cmp(const edge &a,const edge &b){return a.w<b.w;}
int fd(int x){return x==fa[x]?x:fa[x]=fd(fa[x]);}
inline int mrge(int a,int b)
{
	int fda=fd(a),fdb=fd(b);
	if(fda==fdb)return 0;
	fa[fda]=fdb;
	return 1;
}
inline void solve()
{
	ans=tot=0;pre();
	sort(e+1,e+tot+1,cmp);
	for(int i=1;i<=n;++i)fa[i]=i;
	int cnt=0;
	vector<int>v;v.clear();
	for(int i=1;i<=tot;++i)
	{
		if(!mrge(e[i].fr,e[i].to))continue;
		++cnt;ans+=e[i].w;
		v.push_back(i);
		if(cnt==n-1)break;
	}
	int anss=0,st=(1<<q);
	for(int i=1;i<st;++i)
	{
		anss=0;cnt=0;
		for(int j=1;j<=n;++j)fa[j]=j;
		for(int j=0;(1<<j)<=i;++j)
			if(i&(1<<j))
			{
				anss+=val[j];
				for(size_t k=1;k<z[j].size();++k)
					cnt+=mrge(z[j][k],z[j][k-1]);
			}
		for(size_t j=0;j<v.size();++j)
		{
			if(!mrge(e[v[j]].fr,e[v[j]].to))continue;
			++cnt;anss+=e[v[j]].w;
			if(cnt==n-1)break;
		}
		ans=min(ans,anss);
	}
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&q);
		for(int i=0;i<q;++i)
		{
			z[i].clear();
			int num,el;scanf("%d%d",&num,&val[i]);
			for(int j=1;j<=num;++j)
				scanf("%d",&el),
				z[i].push_back(el);
		}
		for(int i=1;i<=n;++i)scanf("%d%d",&x[i],&y[i]);
		solve();
		printf("%d\n",ans);
		if(t)puts("");
	}
	return 0;
}

秦始皇修路(Qin Shi Huang's National Road System, LA5713)
题意:给出一个完全图,边有边权,点有点权。现在要求选择一条边,被选后这条边边权变为0,它两个端点点权之和记为A;同时要求一颗生成树,记这颗生成树的边权和为B。求A/B的最大值。
题解:与上道题类似,先不管这条“免费边”,直接在原图上做一遍MST。之后枚举每一个点对(u,v)尝试将它作为免费边。设MST上u->v的唯一路径上边的最大权值为mx,那么此情况下的ans=(P[u]+P[v])/(w-mx),其中P[i]为i的点权,w为最小生成树的权值,不断更新答案
证明:同样由环路性质:在一个环上,如果某条边的权值最大,那么这条边一定不在MST中。因此当u-v由免费边连接时,形成一个环,去掉其中权值最大的边就行了

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int t,n,tot,fa[N],vis[N];
double x[N],y[N],p[N],d[N][N],w,ans;
vector<int>v[N];
struct edge
{
	int u,v;double w;
	inline bool operator<(const edge &rhs){return w<rhs.w;}
}e[N*N];
inline double dis(int a,int b)
{
	return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
inline void pre()
{
	tot=0;
	for(int i=1;i<=n;++i)
		for(int j=i+1;j<=n;++j)
			e[++tot].u=i,e[tot].v=j,
			e[tot].w=dis(i,j);
	sort(e+1,e+tot+1);
}
int fd(int x){return x==fa[x]?x:fa[x]=fd(fa[x]);}
inline void kruskal()
{
	for(int i=1;i<=n;++i)fa[i]=i,v[i].clear();
	int cnt=0;w=0.0;
	for(int i=1;i<=tot;++i)
	{
		int fda=fd(e[i].u),fdb=fd(e[i].v);
		if(fda==fdb)continue;
		fa[fda]=fdb;
		++cnt;w+=e[i].w;
		v[e[i].u].push_back(e[i].v);
		v[e[i].v].push_back(e[i].u);
		if(cnt==n-1)break;
	}
}
void dfs(int u,int fr)
{
	vis[u]=fr;
	for(size_t i=0;i<v[u].size();++i)
	{
		int _u=v[u][i];
		if(vis[_u]==fr)continue;
		d[fr][_u]=max(d[fr][u],dis(u,_u));
		dfs(_u,fr);
	}
}
inline double solve()
{
	ans=0.0;
	for(int i=1;i<=n;++i)
		for(int j=i+1;j<=n;++j)
			ans=max(ans,(p[i]+p[j])/(w-d[i][j]));
	return ans;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
			scanf("%lf%lf%lf",&x[i],&y[i],&p[i]);
		pre();
		kruskal();
		fill(vis+1,vis+n+1,0);
		memset(d,0,sizeof(d));
		for(int i=1;i<=n;++i)dfs(i,i);
		printf("%.2lf\n",solve());
	}
	return 0;
}

例题 5-21 邦德(Bond, UVa11354)
题意:有n座城市通过m条双向道路相连,每条道路都有一个危险系数。需要回答若干个询问,每个询问包含一个起点s和一个终点t,要求找到一条从s到t的路,使得途径所有边的最大危险系数最小。
题解:求出MST,然后再MST上倍增求得s->t路径上边权的最大值即可
证明:最小生成树是最小瓶颈生成树,因此满足最大边权最小

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5,M=1e5+5;
int n,m,q,tot,fa[N],pr[N][20],dep[N],mx[N][20];
struct edge
{
	int u,v,w;
	edge(int _u=0,int _v=0,int _w=0){u=_u,v=_v,w=_w;}
}e[M];
vector<edge>G[N];
inline bool cmp(const edge &x,const edge &y){return x.w<y.w;}
int fd(int x){return x==fa[x]?x:fa[x]=fd(fa[x]);}
inline bool mrge(int x,int y)
{
	int px=fd(x),py=fd(y);
	if(px==py)return false;
	fa[px]=py;
	return true;
}
inline void kruskal()
{
	for(int i=1;i<=n;++i)fa[i]=i,G[i].clear();
	sort(e+1,e+tot+1,cmp);
	int cnt=0;
	for(int i=1;i<=tot;++i)
	{
		if(!mrge(e[i].u,e[i].v))continue;
		G[e[i].u].push_back(edge(0,e[i].v,e[i].w));
		G[e[i].v].push_back(edge(0,e[i].u,e[i].w));
		if(++cnt==n-1)break;
	}
}
void dfs(int u,int f,int d)
{
	dep[u]=dep[f]+1;
	pr[u][0]=f;mx[u][0]=d;
	for(int i=1;(1<<i)<=dep[u];++i)
		pr[u][i]=pr[pr[u][i-1]][i-1],
		mx[u][i]=max(mx[u][i-1],mx[pr[u][i-1]][i-1]);
	for(size_t i=0;i<G[u].size();++i)
	{
		edge v=G[u][i];
		if(f==v.v)continue;
		dfs(v.v,u,v.w);
	}
}
inline int query(int x,int y)
{
	int anss=0;
	if(dep[x]<dep[y])swap(x,y);
	for(int i=15;i>=0;--i)
		if(dep[pr[x][i]]>=dep[y])
			anss=max(anss,mx[x][i]),
			x=pr[x][i];
	if(x==y)return anss;
	for(int i=15;i>=0;--i)
		if(pr[x][i]!=pr[y][i])
			anss=max(anss,max(mx[x][i],mx[y][i])),
			x=pr[x][i],y=pr[y][i];
	return max(anss,max(mx[x][0],mx[y][0]));
}
int main()
{
	bool first=0;
	while(scanf("%d%d",&n,&m)==2)
	{
		if(first)puts("");
		first=1;tot=0;
		for(int i=1;i<=m;++i)
		{
			int x,y,d;scanf("%d%d%d",&x,&y,&d);
			e[++tot].u=x,e[tot].v=y,e[tot].w=d;
		}
		kruskal();
		memset(pr,0,sizeof(pr));
		memset(mx,0,sizeof(mx));
		dep[0]=0;dfs(1,0,0);
		scanf("%d",&q);
		while(q--)
		{
			int s,t;scanf("%d%d",&s,&t);
			printf("%d\n",query(s,t));
		}
	}
	return 0;
}

Poj2349 Arctic Network
题意:n个前哨,s个卫星频道。如果两个前哨都有卫星频道,则它们可以无视距离地通信。或者使用无线电来通信,最大通信距离为d,所有的d都相同。问在所有前哨都可以直接或间接通信的情况下,最小的d是多少
题解:可以直接做MST。但是不同的是当已加入n-s条边时就停止操作记下答案。需要注意s=0的情况
证明:对于一棵树,每删去一条边,就会多分裂出一个部分。因此当有s个部分时恰满足条件
我的做法:先二分答案,然后对于所有小于二分值的边所连接的两个前哨用并查集合并起来,最后统计有多少个集合是否满足条件就可以了

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=505;
int n,s,p,fa[N];
double x[N],y[N];
inline double dis(int a,int b)
{
	return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
int fd(int x){return x==fa[x]?x:fa[x]=fd(fa[x]);}
inline void mrge(int a,int b)
{
	int pa=fd(a),pb=fd(b);
	if(pa!=pb)fa[pa]=pb;
}
inline bool ck(double k)
{
	for(int i=1;i<=p;++i)fa[i]=i;
	for(int i=1;i<=p;++i)
		for(int j=1;j<i;++j)
			if(dis(i,j)<k)mrge(i,j);
	int cnt=0;
	for(int i=1;i<=p;++i)
		if(fd(i)==i)++cnt;
	if(cnt==1)return true;
	return cnt<=s;
}
int main()
{
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d%d",&s,&p);
		for(int i=1;i<=p;++i)
			scanf("%lf%lf",&x[i],&y[i]);
		double l=0,r=0;
		for(int i=1;i<=p;++i)
			for(int j=i+1;j<=p;++j)
				r=max(r,dis(i,j));
		int u=50;
		while(u--)
		{
			double mid=(l+r)/2;
			if(ck(mid))r=mid;
			else l=mid;
		}
		printf("%.2f\n",l);
	}
	return 0;
}

UVa10600 - ACM Contest and Blackout(最小生成树&次小生成树)

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int t,n,m,tot,ans,anss,fa[N],vis[N],d[N][N];
bitset<N*N>pd;
struct edge
{
	int u,v,w;
	edge(int _u=0,int _v=0,int _w=0){u=_u,v=_v,w=_w;}
}e[N*N];
inline bool cmp(const edge &x,const edge &y){return x.w<y.w;} 
vector<edge>G[N];
void dfs(int rt,int p)
{
	vis[p]=rt;
	for(size_t i=0;i<G[p].size();++i)
	{
		edge v=G[p][i];
		if(vis[v.v]==rt)continue;
		d[rt][v.v]=max(d[rt][p],v.w);
		dfs(rt,v.v);
	}
}
int fd(int x){return fa[x]==x?x:fa[x]=fd(fa[x]);}
inline void solve()
{
	ans=0;pd.reset();
	for(int i=1;i<=n;++i)fa[i]=i;
	sort(e+1,e+tot+1,cmp);
	int cnt=0;
	for(int i=1;i<=n;++i)G[i].clear();
	for(int i=1;i<=tot;++i)
	{
		int pu=fd(e[i].u),pv=fd(e[i].v);
		if(pu==pv)continue;
		fa[pu]=pv;
		ans+=e[i].w;pd[i]=true;
		G[e[i].u].push_back(edge(0,e[i].v,e[i].w));
		G[e[i].v].push_back(edge(0,e[i].u,e[i].w));
		if(++cnt==n-1)break;
	}
	anss=1e9;
	fill(vis+1,vis+n+1,0);
	for(int i=1;i<=n;++i)dfs(i,i);
	for(int i=1;i<=tot;++i)
		if(!pd[i])
			anss=min(anss,ans+e[i].w-d[e[i].u][e[i].v]);
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		tot=0;
		for(int i=1;i<=m;++i)
		{
			int u,v,w;scanf("%d%d%d",&u,&v,&w);
			e[++tot]=edge(u,v,w);
		}
		solve();
		printf("%d %d\n",ans,anss);
	}
	return 0;
}

LOJ10064 黑暗城堡

#include<bits/stdc++.h>
using namespace std;
const int N=1005,mod=2147483647;
int n,m,d[N],cnt[N];
struct edge{int to,w;edge(int _to=0,int _w=0){to=_to,w=_w;}};
vector<edge>E[N];
inline int read()
{
	int s=0,w=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
	return s*w;
}
inline void dij()
{
	fill(d+1,d+n+1,1e9);
	priority_queue<pair<int,int> >pq;
	d[1]=0;
	for(pq.push(make_pair(0,1));!pq.empty();)
	{
		int u=pq.top().second;pq.pop();
		for(size_t i=0;i<E[u].size();++i)
		{
			edge v=E[u][i];
			if(d[v.to]>d[u]+v.w)
			{
				d[v.to]=d[u]+v.w;
				pq.push(make_pair(-d[v.to],v.to));
			}
		}
	}
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=m;++i)
	{
		int x=read(),y=read(),l=read();
		E[x].push_back(edge(y,l));
		E[y].push_back(edge(x,l));
	}
	dij();
	cnt[1]=1;
	for(int i=1;i<=n;++i)
		for(size_t j=0;j<E[i].size();++j)
			if(d[i]+E[i][j].w==d[E[i][j].to])++cnt[E[i][j].to];
	int ans=1;
	for(int i=1;i<=n;++i)
		ans=1ll*ans*cnt[i]%mod;
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-09-13 12:06  BILL666  阅读(108)  评论(0)    收藏  举报