P4926-1007-倍杀测量者解析

P4926 [1007]倍杀测量者

题意:

找到一个最大的T,使得类型1的flag只需要达成 S A ≥ ( k − T ) ∗ S B S_A\ge(k-T)*S_B SA(kT)SB,类型2的flag只需达成 S B ≥ ( k + T ) ∗ S A S_B\ge(k+T)*S_A SB(k+T)SA即可不用女装的情况下,仍然有人需要女装。

分析:

使用二分 + 差分约束

首先从题意可知,如果有一个T使得不等式有解,则此时无人女装。同样,如果有一个T使得不等式无解,则此时至少有一个人需要女装。故我们只需要用二分进行枚举并用spfa判负环判断不等式有解还是无解即可。

其次对差分约束系统进行分析:

由于精度问题,我们需要把原式转化为 l o g ( S A ) ≥ l o g ( k − T ) + l o g ( S B ) log(S_A)\ge log(k-T)+log(S_B) log(SA)log(kT)+log(SB)进行建边即可(另一种类型同理)

所以对于类型1,我们需要从A->B建一条长度为 − l o g ( k − T ) -log(k-T) log(kT)的边。对于类型2,需要从A->B建一条长度为 l o g ( k + T ) log(k+T) log(k+T)的边。

此外,对于已知成绩的学生,相当于 S C = x S_C=x SC=x等价于 S C = x ∗ S 0 S_C=x*S_0 SC=xS0。经过与上述相同步骤的转化,即可从0到学生C建一条 l o g ( x ) log(x) log(x)边,并从学生C到0建一条 − l o g ( x ) -log(x) log(x)的边。

同时,由于仍有同学可能无法访问,需要建一条从每个同学到源点0的长度为0的边。

代码:

#include <bits/stdc++.h>

using namespace std;
#define fast                          \
    ios_base::sync_with_stdio(false); \
    cin.tie(NULL);
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const long long LINF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-5;
struct node {
    int u, v, next;
    double w;
    int type;
} a[M];
int f[N], cnt;
void add(int u, int v, double w, int type) {
    a[++cnt].v = v;
    a[cnt].w = w;
    a[cnt].type = type;
    a[cnt].next = f[u];
    f[u] = cnt;
}
int n, s, t;
double dis[N];
int sta[N], Count[N], st[N], top = 0;
bool spfa(double T){
    top = 0;
    for (int i = 0; i <= n; i++){
        dis[i] = 999999999999;
        Count[i] = 0;
        st[i] = 0;
    }    
    dis[0] = 0;
    queue<int> q;
    q.push(0);
    //sta[++top] = 0;
    st[0] = 1;
    while(!q.empty()){
        //int u = sta[top--];
        int u = q.front();
        q.pop();
        st[u] = 0;
        for (int i = f[u]; i; i = a[i].next){
            int v = a[i].v;
            double w = a[i].w;
            if(a[i].type == 1)    w = -log2(a[i].w - T);
            if(a[i].type == 2)  w = log2(a[i].w + T);
            if(dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                Count[v] = Count[u] + 1;
                if(Count[v] >= n + 2)    return true;
                if(!st[v]){
                    st[v] = 1;
                    //sta[++top] = v;
                    q.push(v);
                }
            }
        }
    }
    return false;
}
int main() {
    cin >> n >> s >> t;
    double l = 0, r = 10;
    for (int i = 1; i <= n; i++)    add(i, 0, 0, 0);
    for (int i = 1; i <= s; i++){
        int type, u, v, w;
        scanf("%d%d%d%d", &type, &u, &v, &w);
        add(u, v, (double)w, type);
    }
    for (int i = 1; i <= t; i++){
        int c, x;
        scanf("%d%d",&c, &x);
        add(c, 0, -log2(x), 0);
        add(0, c, log2(x), 0);
    }
    if(!spfa(0))    puts("-1");
    else{
        double ans = 0;
        while(fabs(l - r) > eps){
            double mid = (l + r) / 2.0;
            if(spfa(mid)){
                ans = mid;
                l = mid;
            }else{
                r = mid;
            }
        }
        printf("%.6f\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值