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
posted @ 2019-03-20 18:31  Rhein_E  阅读(94)  评论(0编辑  收藏  举报