day17T1改错记
题目描述
原题AGC015E
给出\(n\)条不与\(y\)轴平行的直线\(y = k_ix + b_i\),现在要把它们染色
如果两条直线\(l1, l2\)在\(y\)轴右侧有交点,那么染色\(l1\)将同时造成\(l2\)被染色,染色\(l2\)将同时造成\(l1\)被染色,并且影响可以传递(\(l_1\)染色\(l_2\),\(l_2\)又染色\(l_3\)……)
初始你可以选定某些直线染色,然后根据上面的规则可能会有更多直线被染色
问有多少种初始的选择方案,使得最后所有直线被染色?答案对\(1e9 + 7\)取模
\(1 \le N \le 5e5, |k_i|, |b_i| \le 1e9, 保证b_i互不相同\)
解析
如果\(k_i < k_j, b_i > b_j\),那么染色\(i\)必定造成\(j\)被染色,\(k_i > k_j, b_i < b_j\)同理
然后根据染色的“传递性”,可以发现如果把所有直线按\(k\)排序,染色\(i\)会造成\([L_i, R_i]\)区间内的直线被染色
其中\(L_i\)是排序后在\(i\)前面,最远的那条\(b\)比\(i\)大的直线;\(R_i\)是排序后在\(i\)后面,最远的那条\(b\)比\(i\)小的直线;
\(L_i\)和\(R_i\)可以借助树状数组处理出来
然后问题就变成了给出\(n\)个区间,有多少种方案使得\([1, n]\)都被覆盖
然后因为这个题的区间不会出现完全包含(\(L_i < L_j \le R_j < R_i\))的情况,所以可以\(dp\)+前缀和优化(并不知道存在包含的情况可不可以),这一部分具体见代码(如果像我一样以前没见过,手推几组数据大概就理解了……)
然后就没有然后了……
代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define MAXN 500005
typedef long long LL;
const int mod = (int)1e9 + 7;
const int inf = 0x3f3f3f3f;
struct Line {
int k, b;
bool operator <(const Line &l) const { return k == l.k ? b < l.b : k < l.k; }
};
struct Interval {
int l, r;
bool operator <(const Interval &i) const { return l == i.l ? r < i.r : l < i.l; }
};
char gc();
int read();
void prework();
void DP();
int query1(int);
int query2(int);
void update1(int, int);
void update2(int, int);
Line ln[MAXN];
Interval inter[MAXN];
int N, table[MAXN], tot, tree[MAXN], ans;
inline void inc(int &x, int y) { x += y; if (x >= mod) x -= mod; }
inline void dec(int &x, int y) { x -= y; if (x < 0) x += mod; }
inline int add(int x, int y) { x += y; return x >= mod ? x - mod : x; }
inline int sub(int x, int y) { x -= y; return x < 0 ? x + mod : x; }
int main() {
freopen("river.in", "r", stdin);
freopen("river.out", "w", stdout);
N = read();
for (int i = 1; i <= N; ++i)
ln[i].b = read(), ln[i].k = read();
prework();
DP();
printf("%d\n", ans);
}
inline char gc() {
static char buf[1000000], *p1, *p2;
if (p1 == p2) p1 = (p2 = buf) + fread(buf, 1, 1000000, stdin);
return p1 == p2 ? EOF : *p2++;
}
inline int read() {
int res = 0, op; char ch = gc();
while (ch != '-' && (ch < '0' || ch > '9')) ch = gc();
op = (ch == '-' ? ch = gc(), -1 : 1);
while (ch >= '0' && ch <= '9') res = (res << 1) + (res << 3) + ch - '0', ch = gc();
return res * op;
}
void prework() {
for (int i = 1; i <= N; ++i) table[i - 1] = ln[i].b;
std::sort(table, table + N);
tot = std::unique(table, table + N) - table;
for (int i = 1; i <= N; ++i) ln[i].b = std::lower_bound(table, table + N, ln[i].b) - table + 1;
std::sort(ln + 1, ln + N + 1);
memset(tree, inf, sizeof tree);
for (int i = 1; i <= N; ++i) {
update1(ln[i].b, i);
inter[i].l = query1(ln[i].b);
}
memset(tree, 0, sizeof tree);
for (int i = N; i; --i) {
update2(ln[i].b, i);
inter[i].r = query2(ln[i].b);
}
std::sort(inter + 1, inter + N + 1);
}
void update1(int p, int v) {
for (int i = p; i; i -= (i & -i))
tree[i] = std::min(tree[i], v);
}
void update2(int p, int v) {
for (int i = p; i <= tot; i += (i & -i))
tree[i] = std::max(tree[i], v);
}
int query1(int p) {
int res = inf;
for (int i = p; i <= tot; i += (i & -i))
res = std::min(res, tree[i]);
return res;
}
int query2(int p) {
int res = 0;
for (int i = p; i; i -= (i & -i))
res = std::max(res, tree[i]);
return res;
}
void DP() {
static int f[MAXN], s[MAXN];
for (int i = 1, j = 1; i <= N; ++i) {
while (inter[j].r < inter[i].l - 1) ++j;
f[i] = sub(s[i - 1], s[j - 1]);
if (inter[i].l == 1) inc(f[i], 1);
if (inter[i].r == N) inc(ans, f[i]);
s[i] = add(s[i - 1], f[i]);
}
}
//Rhein_E 100pts