题意:给一个固定根的有向图,和cost,每条边有两个权值b,c,要求重新构图使得每个点联通,且总c值不大于cost,求新图的最小的b的最大值。
思路:最小值最大用二分,二分这个答案x,判断的时候先删除每个b小于的x的边,然后再用朱-刘算法求最小生成树的总c值,看其是否大于cost值即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int inf=1e8;
struct node
{
int u,v,b,c;
}a[10005],edge[10005];
int n,m,tot,cost;
int in[65],pre[65],id[65],vis[65];
int DMST(int root,int V,int E)
{
int ret=0;
while(true)
{
for(int i=0;i<V;i++)in[i]=inf;
for(int i=0;i<E;i++)
{
int u=edge[i].u;
int v=edge[i].v;
if(edge[i].c<in[v]&&u!=v)
{
pre[v]=u;
in[v]=edge[i].c;
}
}
for(int i=0;i<V;i++)
if(i!=root&&in[i]==inf)//如果图不连通,无解
return -1;
//找环
int cnt=0;
memset(vis,-1,sizeof(vis));
memset(id,-1,sizeof(id));
in[root]=0;
for(int i=0;i<V;i++)
{
ret+=in[i];
int v=i;
while(vis[v]!=i&&id[v]==-1&&v!=root)//找祖先,要么找到根,要么找到环
{
vis[v]=i;
v=pre[v];
}
if(v!=root&&id[v]==-1)//缩点
{
for(int u=pre[v];u!=v;u=pre[u])id[u]=cnt;
id[v]=cnt++;
}
}
if(cnt==0)//无环
return ret;
for(int i=0;i<V;i++)
if(id[i]==-1)
id[i]=cnt++;
for(int i=0;i<E;i++)//重新构图
{
int u=edge[i].u;
int v=edge[i].v;
edge[i].u=id[u];
edge[i].v=id[v];
if(id[u]!=id[v])
edge[i].c-=in[v];
}
V=cnt;
root=id[root];
}
}
int work(int k)
{
int E=0;
for(int i=0;i<tot;i++)
if(a[i].b>=k)
edge[E++]=a[i];
int flag=DMST(0,n,E);
if(flag==-1||flag>cost)
return 0;
return 1;
}
int main()
{
int T,i,kase=0;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&cost);
int l=0,r=0,mid;
for(i=tot=0;i<m;i++)
{
scanf("%d%d%d%d",&a[tot].u,&a[tot].v,&a[tot].b,&a[tot].c);
if(a[tot].u!=a[tot].v)
{
r=max(r,a[tot].b);
tot++;
}
}
r++;
while(l<r)
{
mid=(l+r)/2;
if(work(mid))
{
if(l==mid)
break;
l=mid;
}
else
r=mid;
}
if(mid==0)
printf("streaming not possible.\n");
else
printf("%d kbps\n",mid);
}
}