[来源未知]百团大战

一、百团大战
有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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值