樱花庄的宠物女孩AtCoder Grand Contest 015E - Mr.Aoki Incubator

44 篇文章 0 订阅

【问题背景】
神田空大养了一群猫,因此被驱逐出了普通宿舍,搬进了樱花庄……
【问题描述】
神田空大养了 n 只猫,这 n 只猫开始时站在一条数轴上,第 i 只猫的位置是
xi,保证 xi 互不相同。
从0 时刻开始,所有猫同时向右走,第 i 只猫的速度为 vi,且自始至终保
持这个速度。很明显,猫之间很可能会发生追及相遇。这里假设一只猫追上了另
一只猫,即某一时刻两只猫在同一个位置,这两只猫将无视碰撞继续保持自己的
速度走下去。
还有一点很明显:经过了无穷的时间后,所有猫都不会再发生碰撞。
现在猫中流行一种感冒,如果一只患了感冒的猫与另一只猫在某一时刻处于
同一位置,那么后者将患上感冒。
每只猫在 0 时刻都有可能患感冒或不患感冒。 神田空大想知道0 时刻 n
2 种情
况中有多少种情况能使所有猫都患上感冒。
【输入格式】
从文件 xtdoor.in 中读入数据。
第一行一个整数 n 表示一共有 n 只猫。
接下来 n 行每行两个整数 xi,vi,表示第 i 只猫在 0 时刻处于位置 xi,且
速度为vi。
【输出格式】
输出到文件 xtdoor.out 中。
一行一个整数,表示0 时刻有多少种情况能使所有猫都患上感冒。

【样例输入】
3
2 5
6 1
3 7

【样例输出】
6

【子任务】
子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以
尝试只解决一部分测试数据。
每个测试点的数据规模及特点如下:
对于 30% 的数据:n≤15
对于 100% 的数据:n≤200000,0 < xi,vi≤1000000000

分析:
这道题可以说是有点难bu以ke理li解yu

首先需要明确,在经过无穷大的时间后
猫猫的排名会由速度决定

一开始我们先按照x排序
针对每一只猫猫,
有两种猫会被ta感染
位于此前但是速度小或者位于此后但是速度大
这相当于一个速度区间

确定每一只猫的感染范围的方法就是
在按x排序后,
找到第i只猫之前的最大速度Ri
和第i只猫之后的最小速度Li,
速度在[Li,Ri]的猫都可以被i感染
(不要试图举出什么反例,这个区间内的猫可能不是被i直接传染,但最终结果都是患病)

现在的问题就变成了已知一些连续的区间,
覆盖[Vmin,Vmax]的所有方案数

显然是dp

因为每只猫的感染区间一定是单调不减的
(感性的理解一下。。。)
那我们把v离散化之后处理出每只猫的感染区间
顺序枚举
f[R[i]]=sigma(f[L[i]]+f[L[i]+1]+…+f[R[i]-1])
//i是猫的编号
写的优美一点
这里写图片描述

tip

真正dp的时候,
我们循环猫的编号
所以这就导致f的意义有了一点变化:f[i]使用前i只猫覆盖整个区间的方案数
sum表示区间和,
用队列记录sum中包含哪些f[i]
每次在进行下一次转移的时候,都暴力的进行sum的修改
说白了就是只要是R小于当前L-1的f值都要减掉
(这一部分是转移不到当前状态的)
最后
f[i]=sum
sum+=f[i]
f[i]入队

这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int mod=1000000007;
const int N=200005;
int g[N],f[N],L[N],R[N],n,tot=0,q[N];
struct node{
    int x,v,vv;
};
node po[N];

int cmp1(const node &a,const node &b){return a.v<b.v;}
int cmp2(const node &a,const node &b){return a.x<b.x;}

void cl()
{
    int i,j;
    int mx=0,mn=tot+1;
    for (i=1;i<=n;i++)  //单调不减 
    {
        mx=(po[i].vv>mx) ? po[i].vv:mx;
        R[i]=mx;
    }
    for (i=n;i>=1;i--)
    {
        mn=(po[i].vv<mn) ? po[i].vv:mn;
        L[i]=mn;
    }
    //for (i=1;i<=n;i++) printf("%d %d\n",L[i],R[i]);
}

void dp()
{
    int sum=0,tou=0,wei=0;
    L[0]=1;R[0]=0;
    f[0]=1;
    q[wei++]=0,sum=1;
    ++n;
    L[n]=R[n]=n;
    for (int i=1;i<=n;i++)  //循环猫的标号 
    {
        while (tou<wei&&L[i]>R[q[tou]]+1)  //用队列 
            sum=sum-f[q[tou]],sum%=mod,++tou;  //区间和 
        f[i]=sum;
        sum=sum+f[i];
        sum%=mod;
        q[wei++]=i;  //加入队列 
    }
    printf("%d",f[n]);
}

int main()
{
    freopen("xtdoor.in","r",stdin);  
    freopen("xtdoor.out","w",stdout);   
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d%d",&po[i].x,&po[i].v);
    sort(po+1,po+1+n,cmp1);
    for (int i=1;i<=n;i++)
        if (po[i].v==po[i-1].v) po[i].vv=po[i-1].vv;
        else po[i].vv=++tot;
    sort(po+1,po+1+n,cmp2);
    cl();
    dp();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值