[BZOJ2285][SDOI2011]保密

bzoj
luogu
不方便简化题意就直接放题面了。

Description

现在,保密成为一个很重要也很困难的问题。如果没有做好,后果是严重的。比如,有个人没有自己去修电脑,又没有拆硬盘,后来的事大家都知道了。
当然,对保密最需求的当然是军方,其次才是像那个人。为了应付现在天上飞来飞去的卫星,军事基地一般都会建造在地下。
某K国的军事基地是这样子的:地面上两排大天井共n1个作为出入口,内部是许多除可以共享出入口外互不连通的空腔,每个空腔有且只有两个出入口,并且这两个出入口不会在同一排。为了方便起见,两排出入口分别编号为1,3,5…和2,4,6…并且最大的编号为n1。
虽然上面扯了那么多关于保密的东西,但是其实解密也是一件很纠结的事情。但其实最简单直接暴力无脑的解密方法就是找个人去看看。。。
我们有很牛X的特种部队,只需要派出一支特种部队到K国基地的某个出入口,那么和这个出入口直接相连的所有空腔都可以被探索,但也只有这些空腔可以被这支部队探索。现在有足够多的特种部队可以供你调遣,你必须使用他们调查完所有的K国基地内的空腔。
当然,你的基地离K国基地不会太近,周边的地图将会给你,表示为n个检查点和m条连接这些点的道路,其中点1到点n1就是K国基地的出入口,点n是你的部队的出发点。对每条道路,有不同的通行时间t和安全系数s。因为情报部门只对单向的道路安全系数进行了评估,所以这些道路只允许单向通行,并且不会存在环。
一支特种部队从你的基地出发,通过某条路径,到达某个K国基地出入口,此时这支部队的危险性表示为总时间和这条路径经过的所有道路的安全系数和的比值。整个行动的危险性表示为你派出的所有部队的危险性之和。你需要使这个值最小的情况下探索整个K国基地。
快点完成这个任务,在K国的叫兽宣布你是K国人之前。

Input

第一行2个正整数n,m (4 <= n <= 700, m <= 100000) 表示整个地区地图上的检查点和道路数。
下面m行,每行4个正整数a, b, t, s(a, b <=n, 1 <= t, s <= 10)表示一条从a到b的道路需时为t,安全系数为s。
接下来1行2个正整数m1和n1(m1 <= 40000, n1 < min{n, 161}), m1表示K国基地空腔的个数,n1表示K国基地出入口的个数。
再接下来m1行,每行2个正整数u, v (u, v<=n1, u是奇数,v是偶数),表示每个空腔的2个出入口。

Output

一行,最小的危险性,保留一位小数。或者输出”-1”(无引号)表示此任务不可能完成。

Sample Input

5 5
5 1 10 1
5 1 10 1
5 2 9 1
5 3 7 1
5 4 8 1
4 4
1 2
1 4
3 2
3 4

Sample Output

17.0

sol

注意到答案是每支特种部队的危险值之和而不是道路长度之和与安全系数之和的比值,所以是不能对答案直接进行分数规划的。

但是我们可以用分数规划求出从起点到每个点的最小危险值。这里写整体二分美滋滋。

之后就是一个二分图的最小权覆盖了。

看上去还是很简单的吧。写起来也挺舒服的

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 705;
const double eps = 1e-5;
const double inf = 1e9;
int n,m,n1,m1,S,T;
struct network{
    struct edge{int to,nxt;double w;}a[N*N];
    int head[N],cnt,dep[N],cur[N];
    queue<int>Q;
    void link(int u,int v,double w){
        a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
        a[++cnt]=(edge){u,head[v],0};head[v]=cnt;
    }
    bool bfs(){
        memset(dep,0,sizeof(dep));
        dep[S]=1;Q.push(S);
        while (!Q.empty()){
            int u=Q.front();Q.pop();
            for (int e=head[u];e;e=a[e].nxt)
                if (a[e].w>eps&&!dep[a[e].to])
                    dep[a[e].to]=dep[u]+1,Q.push(a[e].to);
        }
        return dep[T];
    }
    double dfs(int u,double f){
        if (u==T) return f;
        for (int &e=cur[u];e;e=a[e].nxt)
            if (a[e].w>eps&&dep[a[e].to]==dep[u]+1){
                double tmp=dfs(a[e].to,min(a[e].w,f));
            if (tmp>eps) {a[e].w-=tmp;a[e^1].w+=tmp;return tmp;}
            }
        return 0;
    }
    double dinic(){
        double res=0;
        while (bfs()){
            for (int i=1;i<=T;++i) cur[i]=head[i];
            while (233){
                double tmp=dfs(S,1e9);
                if (tmp<=eps) break;res+=tmp;
            }
        }
        return res;
    }
}G;
struct SPFA{
    struct edge{int to,nxt,t,s;}a[N*N];
    int head[N],cnt,vis[N];double dis[N];
    queue<int>Q;
    void link(int u,int v,int t,int s){
        a[++cnt]=(edge){v,head[u],t,s};head[u]=cnt;
    }
    void spfa(double k){
        memset(dis,0x7f,sizeof(dis));
        dis[n]=0;Q.push(n);
        while (!Q.empty()){
            int u=Q.front();Q.pop();
            for (int e=head[u];e;e=a[e].nxt){
                int v=a[e].to;
                if (dis[v]>dis[u]+a[e].t-k*a[e].s){
                    dis[v]=dis[u]+a[e].t-k*a[e].s;
                    if (!vis[v]) vis[v]=1,Q.push(v);
                }
            }
            vis[u]=0;
        }
    }
}SP;
int p[N],q[N];double ans[N];
void solve(int L,int R,double l,double r){
    if (L>R) return;
    if (r-l<eps){
        for (int i=L;i<=R;++i)
            if (SP.dis[p[i]]>=inf) ans[p[i]]=inf;
            else ans[p[i]]=l;
        return;
    }
    double mid=(l+r)/2;SP.spfa(mid);
    int pl=L-1,pr=R+1;
    for (int i=L;i<=R;++i)
        if (SP.dis[p[i]]<0) q[++pl]=p[i];
        else q[--pr]=p[i];
    for (int i=L;i<=R;++i) p[i]=q[i];
    solve(L,pl,l,mid);solve(pr,R,mid,r);
}
int main(){
    n=gi();m=gi();
    while (m--){
        int u=gi(),v=gi(),t=gi(),s=gi();
        SP.link(u,v,t,s);
    }
    m1=gi();n1=gi();S=n1+1;T=S+1;G.cnt=1;
    for (int i=1;i<=n1;++i) p[i]=i;
    solve(1,n1,0,10);
    for (int i=1;i<=n1;++i)
        if (i&1) G.link(S,i,ans[i]);
        else G.link(i,T,ans[i]);
    while (m1--){
        int u=gi(),v=gi();
        if (ans[u]==inf&&ans[v]==inf) return puts("-1"),0;
        G.link(u,v,inf);
    }
    printf("%.1lf\n",G.dinic());return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/9086856.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值