一、百团大战
有n个节目,其描述了在Ti时刻Xi号社团会表演节目(持续时间忽略不计)。而LMZ在一单位时间内最多也只能跑过V个社团的距离(比如从1号社团跑到V+1号社团),而最少则可以不动,跑步的左右方向任意。他想知道:
1.当他初始时刻是站在0号社团的情况下,他最多能看到多少节目?
2.当他初始时刻可以站在任意位置的情况下,他最多能看到多少节目?
注:初始时刻指的是时间为0.
【输入格式】fight.in
第一行仅1个正整数n,为节目数量。
接下来n行每行2个正整数Xi,Ti,为第i个节目的属性。输入数据保证不会有重复的节目。
最后一行一个正整数V,表示LMZ的速度上限。
【输出格式】fight.out
仅2个正整数,分别为问题1和问题2的答案。
样例输入 样例输出
3
6 1
1 3
4 4
4 2 3
【样例解释】
若一开始LMZ站在0号位置,那么在时间为1时是不可能跑到6号社团的,但是后面的2个表演可以看得到;而若一开始便站在6号社团处,便3个表演都看得到。
【数据范围与约定】
对于10%的数据:n<=10
对于40%的数据:n<=3000
对于100%的数据:1<=n<=100000,-2*108<=Xi<=2*108,1<=V<=1000,1<=Ti<=106
想到一个暴力dp,先把节目按照时间排序,然后abs(a[i].x-a[j].x)<=(a[i].t-a[j].t)*v则可以从j社团转移到i社团观看。
有abs不好做,我们考虑拆开
a[i].x>a[j].x
a[i].x-a[j].x<=a[i].t*v-a[j].t*v
a[i].t*v-a[i].x>=a[j].x-a[j].t*v
a[i].x < a[j].x
a[j].x-a[i].x<=a[i].t*v-a[j].t*v
a[i].t+a[i].x>=a[j].x+a[j].t*v
发现问题比较棘手,要保证t的大小的同时,也要保证x的大小,又要使得式子成立,于是很自然的想到cdq分治。将区间中的t排序,则右区间显然不会更新左区间,那么先计算左区间贡献,然后用左区间更新右区间,再更新右区间。
先用两个数组保存上面2式子的值(很大要离散),然后i可以被式子值小于他的更新。这里可以用树状数组做前缀最大值,正反做2遍,问题就得到了解决。
其中2个问题是初值赋的不一样,具体的可以看代码感受一下。
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
const int N=110000;
const int M=410000;
int v,n,m,ans,Max;
int sum[N];
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
int top;
struct cc
{
int x,t,del,add,f;
}a[N];
inline void insert(int x, int y)
{
for(int i=x;i<=Max;i+=(i&-i)) sum[i]=max(sum[i], y);
}
inline void del(int x)
{
for(int i=x;i<=Max;i+=(i&-i)) sum[i]=0;
}
inline int query(int x)
{
int res=0;
for(int i=x;i;i-=(i&-i)) res=max(res,sum[i]);
return res;
}
inline bool cmp_del(const cc &a,const cc &b)
{
return a.del<b.del;
}
inline bool cmp_add(const cc &a,const cc &b)
{
return a.add<b.add;
}
inline bool cmp_t(const cc &a,const cc &b)
{
return a.t<b.t;
}
inline bool cmp_x(const cc &a,const cc &b)
{
return a.x<b.x;
}
inline void work(int l,int r)//t从小到大 考虑让x有序 用左边更新右边
{
if(l==r) return;
int mid=l+r>>1;
sort(a+l,a+1+r,cmp_t);
work(l,mid);
sort(a+l,a+1+mid,cmp_x);
sort(a+mid+1,a+1+r,cmp_x);
int j=l;
for(int i=mid+1;i<=r;++i)
{
while(a[j].x<=a[i].x&&j<=mid)
{
if(a[j].f) insert(a[j].del,a[j].f+1);
++j;
}
a[i].f=max(a[i].f,query(a[i].del));
}
for(int i=l;i<=mid;++i) if(a[i].f) del(a[i].del);
j=mid;
for(int i=r;i>=mid+1;--i)
{
while(a[j].x>a[i].x&&j>=l)
{
if(a[j].f) insert(a[j].add,a[j].f+1);
--j;
}
a[i].f=max(a[i].f,query(a[i].add));
}
for(int i=l;i<=mid;++i) if(a[i].f) del(a[i].add);
work(mid+1,r);
}
int past[N];
int main()
{
freopen("fight.in","r",stdin);
freopen("fight.out","w",stdout);
n=read();
for(int i=1;i<=n;++i)
{
a[i].x=read();
a[i].t=read();
}
v=read();
for(int i=1;i<=n;++i)
{
a[i].del=a[i].t*v-a[i].x;
a[i].add=a[i].t*v+a[i].x;
}
sort(a+1,a+1+n,cmp_del);
past[1]=a[1].del;
a[1].del=1;
for(int i=2;i<=n;++i)
{
past[i]=a[i].del;
if(a[i].del!=past[i-1]) a[i].del=a[i-1].del+1;
else a[i].del=a[i-1].del;
}
Max=a[n].del;
sort(a+1,a+1+n,cmp_add);
past[1]=a[1].add;
a[1].add=1;
for(int i=2;i<=n;++i)
{
past[i]=a[i].add;
if(a[i].add!=past[i-1]) a[i].add=a[i-1].add+1;
else a[i].add=a[i-1].add;
}
Max=max(Max,a[n].add);
for(int i=1;i<=n;++i)
if(abs(a[i].x)<=a[i].t*v) a[i].f=1;
work(1,n);
for(int i=1;i<=n;++i)
ans=max(ans,a[i].f);
printf("%d",ans);
for(int i=1;i<=n;++i) a[i].f=1;
ans=0;
work(1,n);
for(int i=1;i<=n;++i)
ans=max(ans,a[i].f);
printf(" %d",ans);
}