【GMOJ4017】逃跑

题目

题目链接:https://gmoj.net/senior/#main/show/4017
Konrad, Delfador 和 Kalenz 一行人又喜闻乐见地被追杀了。
他们面临的是一条有 N 个地点的路, 他们从 0 号地点出发, 要逃到 N 号地点去。每个地点的战斗都有一定的金币收入 Ai,也有一定的部队损失 Bi。
为了更好地逃生, Delfador 还弄到了一块传送宝石,这样一行人就能向后传送不超过 L 的距离。从一个地点传送到另一个地点时,Konrad 会选择路径上除起点外的地形指数 Ci 最大的地点进行战斗,地形指数相同时选择最靠后的。
作为优秀的领导者, Konrad 希望总金币收入与总部队损失的比值最大。

思路

如果没有长度\(L\)的限制,以及找\(c\)最大的地方打架的限制,那么这道题就是一个裸的\(01\)分数规划的问题。
有了这些限制后依然一样,先二分\(mid\)表示\(\frac{\sum A_i}{\sum B_i}\geq mid\),然后判断能否选出一些\(i\)满足上式。
那么我们要让\(\sum (A_i-mid\times B_i)\)大于零。
设\(f[i]\)表示跳到点\(i\)时,\(\sum (A_j-mid\times B_j)\)的最大值,其中\(j\)是选择的落脚点。
那么先预处理出每一个点的影响区间\([l_i,r_i]\),意思是在\([l_i,r_i]\)的点如果跳到点\(i\)后面,将会在\(i\)处打架。
这个可以用平衡树、线段树、\(bit\)、单调队列+二分等东西在\(O(n\log n)\)的时间复杂度内求出来,具体方法留作思考(雾
那么显然有转移
\[f[i]=max(f[j]+A_k-B_k\times mid)\],\(k\)应该为\((j,i]\)中\(c\)最大且尽量靠后的那一个。
如果\(f[n]\geq 0\),那么\(mid\)就过小,否则过大。

代码

特别丑+调到自闭。

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=300010,Inf=1e9;
int n,m,a[N],b[N],c[N],d[N],L[N],R[N];
double f[N];

struct Segnode
{
    int l,r,mn;
};

struct SegTree
{
    Segnode tree[N*4];

    void pushup(int x)
    {
        tree[x].mn=max(tree[x*2].mn,tree[x*2+1].mn);
    }

    void build(int x,int l,int r,int val)
    {
        tree[x].l=l; tree[x].r=r; tree[x].mn=val;
        if (l==r) return;
        int mid=(l+r)>>1;
        build(x*2,l,mid,val);
        build(x*2+1,mid+1,r,val);
    }

    int ask(int x,int l,int r)
    {
        if (tree[x].l==l && tree[x].r==r)
            return tree[x].mn;
        int mid=(tree[x].l+tree[x].r)>>1;
        if (r<=mid) return ask(x*2,l,r);
        if (l>mid) return ask(x*2+1,l,r);
        return max(ask(x*2,l,mid),ask(x*2+1,mid+1,r));
    }

    void update(int x,int k,int val)
    {
        if (tree[x].l==k && tree[x].r==k)
        {
            tree[x].mn=val;
            return;
        }
        int mid=(tree[x].l+tree[x].r)>>1;
        if (k<=mid) update(x*2,k,val);
            else update(x*2+1,k,val);
        pushup(x);
    }
}seg;

struct Segnode2
{
    int l,r;
    double maxn,f,k,lazy;
};

struct SegTree2
{
    Segnode2 tree[N*4];

    void pushup(int x)
    {
        tree[x].maxn=max(tree[x*2].maxn,tree[x*2+1].maxn);
        tree[x].f=max(tree[x*2].f,tree[x*2+1].f);
    }

    void pushdown(int x)
    {
        if (tree[x].lazy)
        {
            tree[x*2].maxn=tree[x*2].f+tree[x].lazy;
            tree[x*2+1].maxn=tree[x*2+1].f+tree[x].lazy;
            tree[x*2].k=tree[x*2].lazy=tree[x].lazy;
            tree[x*2+1].k=tree[x*2+1].lazy=tree[x].lazy;
            tree[x].lazy=0;
        }
    }

    void build(int x,int l,int r)
    {
        tree[x].l=l; tree[x].r=r; tree[x].lazy=0;
        tree[x].maxn=tree[x].f=tree[x].k=-100000000000.0;
        if (l==r) return;
        int mid=(l+r)>>1;
        build(x*2,l,mid);
        build(x*2+1,mid+1,r);
    }

    double ask(int x,int l,int r)
    {
        if (l>r) return -1000000000000.0;
        if (tree[x].l==l && tree[x].r==r)
            return tree[x].maxn;
        pushdown(x);
        int mid=(tree[x].l+tree[x].r)>>1;
        if (r<=mid) return ask(x*2,l,r);
        if (l>mid) return ask(x*2+1,l,r);
        return max(ask(x*2,l,mid),ask(x*2+1,mid+1,r));
    }

    void updatef(int x,int k,double val)
    {
        if (tree[x].l==k && tree[x].r==k)
        {
            tree[x].f=val;
            return;
        }
        int mid=(tree[x].l+tree[x].r)>>1;
        if (k<=mid) updatef(x*2,k,val);
            else updatef(x*2+1,k,val);
        pushup(x);
    }

    void updatek(int x,int l,int r,double val)
    {
        if (tree[x].l==l && tree[x].r==r)
        {
            tree[x].k=tree[x].lazy=val;
            tree[x].maxn=tree[x].f+tree[x].k;
            return;
        }
        pushdown(x);
        int mid=(tree[x].l+tree[x].r)>>1;
        if (r<=mid) updatek(x*2,l,r,val);
        else if (l>mid) updatek(x*2+1,l,r,val);
        else updatek(x*2,l,mid,val),updatek(x*2+1,mid+1,r,val);
        pushup(x);
    }
}seg2;

bool check(double mid)
{
    seg2.build(1,0,n);
    seg2.updatef(1,0,0.0);
    for (int i=1;i<=n;i++)
    {
        seg2.updatek(1,L[i],i-1,1.0*a[i]-1.0*b[i]*mid);
        f[i]=seg2.ask(1,max(i-m,0),i-1);
        seg2.updatef(1,i,f[i]);
    }
    return f[n]>=0.0;
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&a[i],&b[i],&c[i]);
        d[i]=c[i];
    }
    sort(d+1,d+1+n);
    int cnt=unique(d+1,d+1+n)-d-1;
    for (int i=1;i<=n;i++)
        c[i]=lower_bound(d+1,d+1+cnt,c[i])-d;
    seg.build(1,1,cnt+1,0);
    for (int i=1;i<=n;i++)
    {
        L[i]=max(max(0,i-m),seg.ask(1,c[i]+1,cnt+1));
        seg.update(1,c[i],i);
    }
    double l=0.00001,r=1000000.0,mid;
    while (r-l>0.0000000003)
    {
        mid=(l+r)/2.0;
        if (check(mid)) l=mid;
            else r=mid;
    }
    cnt=0;
    for (;l>=10.0;cnt++) l/=10.0;
    for (;l<=1.0;cnt--) l*=10.0;
    printf("%0.9lfe",l);
    if (cnt<0) printf("-");
        else printf("+");
    cnt=abs(cnt);
    if (cnt<10) printf("00%d",cnt);
    else if (cnt<100) printf("0%d",cnt);
    else printf("%d",cnt);
    return 0;
}

原文地址:https://www.cnblogs.com/stoorz/p/12283910.html

时间: 2024-11-09 00:12:15

【GMOJ4017】逃跑的相关文章

猫大叫一声,所有的老鼠都开始逃跑,主人被惊醒

程序设计: 猫大叫一声,所有的老鼠都开始逃跑,主人被惊醒.(C#语言)要求:  1.要有联动性,老鼠和主人的行为是被动的.2.考虑可扩展性,猫的叫声可能引起其他联动效应. 要点:1. 联动效果,运行代码只要执行Cat.Cryed()方法.    2. 对老鼠和主人进行抽象 1 public interface Observer 2 { 3 void Response(); //观察者的响应,如是老鼠见到猫的反映 4 } 5 public interface Subject 6 { 7 void

逃跑(escape)

逃跑(escape) 时间限制: 3 Sec  内存限制: 128 MB 题目描述 输入 第一行是5个正整数,n,m,k,S,T,分别代表无向图点数,边数,蝙蝠的数量,二小姐所在起点的编号,目标点的编号. 第二行是k个正整数,分别代表大小姐每个蝙蝠所在的起点的编号.接下来有m行,每行有4个正整数,u,v,q,p,分别是该边的起点.终点,蝙蝠通过该 路花费的代价,二小姐通过该路花费的代价. 输出 一行,一个整数,所有人物达到终点所需要的代价的和的最小值. 样例输入 5 5 2 3 4 1 5 1

WIKIOI 1026 逃跑的拉尔夫 深度优先搜索

/* 1026 逃跑的拉尔夫 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 年轻的拉尔夫开玩笑地从一个小镇上偷走了一辆车,但他没想到的是那辆车属于警察局,并且车上装有用于发射车子移动路线的装置. 那个装置太旧了,以至于只能发射关于那辆车的移动路线的方向信息. 编写程序,通过使用一张小镇的地图帮助警察局找到那辆车.程序必须能表示出该车最终所有可能的位置. 小镇的地图是矩形的,上面的符号用来标明哪儿可以行车哪儿不行.“.”表示小镇

程序设计: 猫大叫一声,所有的老鼠都开始逃跑,主人被惊醒

第一种解决方案用事件来解决:是多级的联动:即:猫叫->老鼠跑->人醒 1 public class Cat 2 { 3 public delegate void Crying(object sender,EventArgs e)://定义一个猫叫委托 4 public event Crying cry://定义猫叫事件 5 public void OnCry(EventArgs e) 6 { 7 if(cry!=null) 8 { 9 cry(this,e): 10 } 11 } 12 pub

对话框捕获鼠标移动消息,实现“逃跑按钮”

在弹出的对话框上显示逃跑按钮,当鼠标移动到该按钮上时,该按钮隐藏,而在别的位置上显示一个一模一样的按钮,鼠标再次置于新按钮上时,该按钮再次隐藏而在另一位置显示,用户感觉按钮好像在自动逃跑移动: 1.新建一个基于对话框的MFC工程,将其资源窗口的对话框中的自带控件删除,在不同位置添加两个一模一样的按钮控件 2.此处需要让按钮来捕获鼠标移动的消息,但是该对话框中有两个一样的按钮,故需要创建一个派生于CButton类的新的类CNewButton,为两个按钮控件分别添加成员变量并与CNewButton类

codevs 1026 逃跑的拉尔夫 x

1026 逃跑的拉尔夫 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 年轻的拉尔夫开玩笑地从一个小镇上偷走了一辆车,但他没想到的是那辆车属于警察局,并且车上装有用于发射车子移动路线的装置. 那个装置太旧了,以至于只能发射关于那辆车的移动路线的方向信息. 编写程序,通过使用一张小镇的地图帮助警察局找到那辆车.程序必须能表示出该车最终所有可能的位置. 小镇的地图是矩形的,上面的符号用来标明哪儿可以行车哪儿不行.“.”表示小镇上那块

MFC学习——“逃跑”按钮的实现

如何设计对话框程序上的"逃跑"按钮,即每当鼠标移动到Btn上面的时候就会自动跑到另外一个地方,看上去鼠标好像永远无法点中? 讨巧:设置鼠标WM_MOUSEMOVE的响应函数用于捕获鼠标移动操作,每当鼠标移动到btn上就修改属性隐藏,同时修改另一个btn的属性为显示,造成btn自动"逃跑"的假象. 步骤: 1.创建基于对话框的应用程序 新建-MFC项目-选择"基于对话框"就可以了,然后就会得到含有"OK"和"Cance

AC日记——逃跑的拉尔夫 codevs 1026 (搜索)

1026 逃跑的拉尔夫 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 年轻的拉尔夫开玩笑地从一个小镇上偷走了一辆车,但他没想到的是那辆车属于警察局,并且车上装有用于发射车子移动路线的装置. 那个装置太旧了,以至于只能发射关于那辆车的移动路线的方向信息. 编写程序,通过使用一张小镇的地图帮助警察局找到那辆车.程序必须能表示出该车最终所有可能的位置. 小镇的地图是矩形的,上面的符号用来标明哪儿可以行车哪儿不行.“.”表示小镇

1026 逃跑的拉尔夫

1026 逃跑的拉尔夫 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 年轻的拉尔夫开玩笑地从一个小镇上偷走了一辆车,但他没想到的是那辆车属于警察局,并且车上装有用于发射车子移动路线的装置. 那个装置太旧了,以至于只能发射关于那辆车的移动路线的方向信息. 编写程序,通过使用一张小镇的地图帮助警察局找到那辆车.程序必须能表示出该车最终所有可能的位置. 小镇的地图是矩形的,上面的符号用来标明哪儿可以行车哪儿不行.“.”表示小镇上那块