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