樱花庄的宠物女孩AtCoder Grand Contest 015E

来源:互联网 发布:java项目经验描述 编辑:程序博客网 时间:2024/04/28 07:06

【问题背景】
神田空大养了一群猫,因此被驱逐出了普通宿舍,搬进了樱花庄……
【问题描述】
神田空大养了 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;}