备战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;
}

浙公网安备 33010602011771号