AtCoder Grand Contest 015 E Mr.Aoki Incubator
题意
在数轴上有\(n\)个人,分别在\(x_i\)处以\(v_i\)的速度向数轴正方向移动,当一个没有染色的人和染色的人经过时,没有染色的人会被染上颜色;现在在移动之前给某些人提前染上色,问共有多少种染色方式,使得在足够长的时间过后,所有人都能被染上色;
题解
时间足够长时,这\(n\)个人的顺序一定是确定的,也就是他们按速度排序过后的顺序;所以我们先将这\(n\)个人按速度从小到大排序;有个显然的结论是:如果我们给某个人染色,那么颜色一定会传染给速度比他小且初始位置比他前以及速度比他大且初始位置比他后的人;接下来对于每个人,我们找到初始位置大于等于他且速度最小的人\(L\),以及初始位置小于等于他且速度最大的人\(R\),如果我们初始选择给这个人染色,\(L,R\)都可以是他自己,那么这个人能一定能传染完且仅能传染完\([L,R]\)区间的人;
证明:
这个区间中有\(5\)类人:
- 他自己,记为\(A\):初始已被染色;
- 速度比他慢,初始位置在他之前的人,记为\(B\):如果\(L\)不是\(A\)则一定是这类人,会被\(A\)追上并被染色;
- 速度比他慢,初始位置在他之后的人,记为\(C\):\(C\)必然不是\(L\),并且如果\(C\)存在则\(B\)一定也存在,某个\(B\)会成为\(L\),因为\(C\)的速度一定比\(L\)快,所以\(L\)会被\(A\)染色,然后\(C\)会追上\(L\)并被染色;
- 速度比他快,初始位置在他之后的人,记为\(D\):如果\(R\)不是\(A\)则一定是这类人,会追上\(A\)并被染色;
- 速度比他快,初始位置在他之前的人,记为\(E\):\(E\)必然不是\(R\),并且如果\(E\)存在则\(D\)一定也存在,某个\(D\)会成为\(R\),因为\(E\)的速度一定比\(R\)慢,所以\(R\)会被\(A\)染色,然后\(R\)会追上\(E\)并把\(E\)染色;
所以现在我们的问题转换成了:有\(n\)个区间,问有多少种选择区间的方式,使得被选区间的并的长度等于\(n\);并且发现因为这些区间扩展的规则很特殊,所以这\(n\)个区间按右端点排序(右端点相同按左端点排序)后,随着右端点的增加,左端点是单调递增的;所以我们定义\(F[i][0/1]\)表示使用到第\(i\)个区间并且一定用了第\(i\)个区间\(0:不能把目前涉及到的区间覆盖完/1:能把目前涉及到的区间覆盖完\);转移的话,如果\(i\)区间的左端点为\(1\)则$$F[i][1]=1+\sum_{j,区间j的右端点小于等于区间i的左端点-1}F[j][0]+F[j][1]$$且$$F[i][0]=0$$
否则$$F[i][1]=\sum_{j,区间j的右端点小于等于区间i的左端点-1}F[j][1]$$
且$$F[i][0]=1+\sum_{j,区间j的右端点小于等于区间i的左端点-1}F[j][0]$$然后因为随着右端点的增长左端点单调递增,所以求个前缀和就能\(O(n)\)做这个DP了,注意在右端点等于\(n\)的时候统计答案
#include<bits/stdc++.h>
#define Fst first
#define Snd second
#define RG register
#define mp make_pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef long double LD;
typedef unsigned int UI;
typedef unsigned long long ULL;
template<typename T> inline void read(T& x) {
char c = getchar();
bool f = false;
for (x = 0; !isdigit(c); c = getchar()) {
if (c == '-') {
f = true;
}
}
for (; isdigit(c); c = getchar()) {
x = x * 10 + c - '0';
}
if (f) {
x = -x;
}
}
template<typename T, typename... U> inline void read(T& x, U& ... y) {
read(x), read(y...);
}
const int N=2e5+10,P=1e9+7;
int n;
int ST[N],F[N][2];
struct Data {
int x,v;
Data () {}
Data (int x,int v) :x(x),v(v) {}
}A[N];
bool cmp(Data A,Data B) {
return A.v<B.v;
}
struct Sec {
int l,r;
Sec () {}
Sec (int l,int r) :l(l),r(r) {}
}G[N];
bool cmp1(Sec A,Sec B) {
return A.r==B.r?A.l<B.l:A.r<B.r;
}
//#define rua
int main() {
// ios::sync_with_stdio(false);
#ifdef rua
freopen("GG.in","r",stdin);
#endif
read(n);
for(int i=1;i<=n;++i) read(A[i].x,A[i].v);
sort(A+1,A+n+1,cmp);
int top=0;
for(int i=1;i<=n;++i) {
if(!top||A[i].x>A[ST[top]].x) ST[++top]=i;
int l=1,r=top;
while(l!=r) {
int mid=l+r>>1;
if(A[ST[mid]].x>=A[i].x) r=mid;
else l=mid+1;
}
G[i].l=ST[l];
}
top=0;
for(int i=n;i;--i) {
if(!top||A[i].x<A[ST[top]].x) ST[++top]=i;
int l=1,r=top;
while(l!=r) {
int mid=l+r>>1;
if(A[ST[mid]].x<=A[i].x) r=mid;
else l=mid+1;
}
G[i].r=ST[l];
}
sort(G+1,G+n+1,cmp1);
int res=0; top=0;
for(int i=1;i<=n;++i) {
if(G[i].l==1) F[i][1]=(F[i-1][0]+F[i-1][1]+1)%P;
else {
while(top<i&&G[top+1].r<G[i].l-1) ++top;
F[i][1]=(F[i-1][1]-F[top][1]+P)%P;
F[i][0]=(F[i-1][0]-F[top][0]+P+1)%P;
}
if(G[i].r==n) res=(res+F[i][1])%P;
F[i][1]=(F[i-1][1]+F[i][1])%P;
F[i][0]=(F[i-1][0]+F[i][0])%P;
}
printf("%d\n",res);
return 0;
}