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





