CF76F. Tourist

博客介绍了如何解决数轴上的事件观察问题,通过将事件转化为二维坐标并利用最长上升子序列(LIS)进行优化,解决速度限制下的最大事件观察数。内容包括题目解析、思路分享及优化方法。

题目链接:CF76F. Tourist


超级赞的一个题!!!

题目大意: 给出一个数轴和 n(n<=10^5)事件,每个事件都有(xi,ti) 表示第i个事件在位置xi处事件ti时刻发生,然后给出V表示该人的速度即使1s中最多能走了V单位长度。1.问如果该人开始在0位置0时刻出发,最多能看到多少事件。 2 。该人在任何位置0时刻出发最多能够看到多少事件


思路: 开始拿到这个题目一开始第一想法就是把每个事件按事件ti排序,然后用类似背包的的思路搞这个题,这样的话事件复查度是O(n^2) , dp [ (x[ i ] , t[ i ]) ] = max( dp[ ( x[ j ] , t[ j ])] , j<i &&  | x[i]-x[j]  | <= V*(ti - tj) ,然后想到了用数据结构来优化状态转移但是发现有| x[i]-x[j]  | <= V*(ti - tj ) 的限制条件几乎没法优化,然后就搞不出来……

            看了解题报告 | x[i]-x[j]  | <= V*(ti - tj)  这个不等式可以拆开成两个不等式 1.  V*t[i] + x[i ]  >= V*t[j] + x[j]   2. V*t[i] - x[i]  >= V*t[j] - x[j]  ,然后惊奇的发现每个事件可以映射成一个二维的坐标 ( V*t[i ]  + x[i]  , V*t[i ]  - x[i] ) ,然后就是LIS最长上升子序列问题了,想到这这个题目差不多完成了以大步

          结合题目: 第一问要求起初的状态必须是(0,0),那么仔细分析发现映射后的坐标只能去第一象限的点

                              第二问对于起始点没有要求,直接可以随便搞了


#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <set>

using namespace std;

const int maxn=201000;
typedef long long ll;
struct Point
{
    ll x,y;
    bool operator<(Point a)const
    {
        if(x!=a.x) return x < a.x;
        return y < a.y;
    }
}p[maxn];
int x[maxn],t[maxn];

int LIS(int n)
{
    multiset<ll> st;
    multiset<ll>::iterator it;
    for(int i=0;i<n;i++)
    {
        it=st.upper_bound(p[i].y);
        if(it!=st.end()) st.erase(it);
        st.insert(p[i].y);
    }
    return st.size();
}
int main()
{
    int n;
    long long V;
    while(scanf("%d",&n)==1)
    {
        for(int i=0;i<n;i++) scanf("%d%d",&x[i],&t[i]);
        scanf("%I64d",&V);
        int tot=0;
        for(int i=0;i<n;i++)
          if(V*t[i]+x[i]>=0&&V*t[i]-x[i]>=0)
          {
              p[tot].x=V*t[i]+x[i];
              p[tot++].y=V*t[i]-x[i];
          }
        sort(p,p+tot);
       /* for(int i=0;i<tot;i++)
          cout<<p[i].x<<" "<<p[i].y<<endl;*/
        int ans1=LIS(tot);
        for(int i=0;i<n;i++)
          {
              p[i].x=V*t[i]+x[i];
              p[i].y=V*t[i]-x[i];
          }
        sort(p,p+n);
        int ans2=LIS(n);
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值