E - Mr.Aoki Incubator
题意:
数轴上有N个黑点,每个点都有一个方向向右的正速度v。当两个点在同一个位置上重合时,若其中一个是红色,另一个也变成红色。保证没有相同速度或初始坐标。现问你有多少方法染红一些点,使得无穷久后所有点都被染红。 N≤200000
分析:
首先按照速度v从大到小排序,对于一个点,它左边的点中,坐标x第一个比它的坐标小的,设为L;右边的点中,坐标最后一个比它的坐标大的,设为R。
那么如果这个点被染色,R-L+1区间的点都将被染色,预处理出每个点对应的区间。问题变成选一些区间,覆盖1到n的问题,dp一下。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 200005, mod = 1e9 + 7; int F[N], sum[N], mn[N], mx[N]; struct Edge{ int v, l, r, x; } A[N]; bool cmp1(const Edge &A,const Edge &B) { return A.v > B.v; } bool cmp2(const Edge &A,const Edge &B) { return A.l == B.l ? A.r < B.r : A.l < B.l; } // 左端点相同时的排序!!! int main() { int n = read(); for (int i = 1; i <= n; ++i) A[i].x = read(), A[i].v = read(); sort(A + 1, A + n + 1, cmp1); mn[1] = A[1].x; for (int i = 2; i <= n; ++i) mn[i] = min(mn[i - 1], A[i].x); for (int i = 1; i <= n; ++i) { int l = 1, r = i - 1, p = i; while (l <= r) { int mid = (l + r) >> 1; if (mn[mid] < A[i].x) p = mid, r = mid - 1; else l = mid + 1; } A[i].l = p; } mx[n] = A[n].x; for (int i = n - 1; i >= 1; --i) mx[i] = max(mx[i + 1], A[i].x); for (int i = n; i >= 1; --i) { int l = i + 1, r = n, p = i; while (l <= r) { int mid = (l + r) >> 1; if (mx[mid] > A[i].x) p = mid, l = mid + 1; else r = mid - 1; } A[i].r = p; } sort(A + 1, A + n + 1, cmp2); int p = 1, Ans = 0; for (int i = 1; i <= n; ++i) { while (A[p].r < A[i].l - 1) p ++; F[i] = (sum[i - 1] - sum[p - 1] + mod) % mod; if (A[i].l == 1) F[i] = (F[i] + 1) % mod; sum[i] = (sum[i - 1] + F[i]) % mod; if (A[i].r == n) Ans = (Ans + F[i]) % mod; } cout << Ans; return 0; }