【luogu P8375】游戏(思维)(线段树)
游戏
题目链接:luogu P8375
题目大意
给你 n 个点排成一列,前面 k 个是特殊点,而且每个特殊点会连一条有向边向下一个特殊点(如果下一个不是特殊点就不连)。
然后每次会加一条有向边,然后问你在什么时候开始出现了一个环包含了特殊点。
思路
首先要知道普通图加边动态判环好像是 NP 还是 NP-hard 的,所以我们考虑它有的特殊性质。
也就是前面的一段边。
那我们发现只要前面的那些特殊点能从 \(i\) 走到 \(j\) 而且 \(i\geqslant j\) 即可。
那我们考虑对于每个点维护 \(L_i,R_i\) 分别表示到能到它的编号最大的特殊点,和它能到的编号最小的特殊点。
那不难看出如果你在加边 \(x\rightarrow y\) 的时候发现 \(L_x\geqslant R_y\) 那就找到环了。
(只用看这个位置是因为如果别的地方满足了它之前一定没有满足,那它一定是走这里满足的,所以这里一定也是会满足的)
那问题就是你要怎么维护,通过势能分析我们会发现如果硬要维护每个位置的 \(L,R\) 一定是不行的。
那我们考虑怎么维护,其实我们会发现上面那个条件可以看做是自己的区间是否是空的,有这么一个想法就是因为你只需要保证这个区间空或否即可。
那就有一个想法就是你给这个区间弄一棵线段树之类的(不用真的建出来),然后每次只有当更新的值超过了中间线,你才更新左右端点。
那这样每个点的区间弄 \(\log n\) 次,那每次都修改至多 \(n\log n\) 也就可以通过了。
代码
#include<cstdio>
#include<vector>
using namespace std;
const int N = 3e5 + 100;
int n, k, L[N], R[N];
//L[i] 到它的最大 R[i] 能到的最小
vector <int> G[N], G_[N];
bool fin;
void init(int n_, int k_) {
n = n_; k = k_; fin = 0;
for (int i = 1; i <= n; i++) G[i].clear(), G_[i].clear();
for (int i = 1; i <= n; i++) {
if (i <= k) L[i] = R[i] = i;
else L[i] = 0, R[i] = k + 1;
}
}
void check(int x, int y);
void update(int x) {
for (int i = 0; i < G[x].size(); i++) check(x, G[x][i]);
for (int i = 0; i < G_[x].size(); i++) check(G_[x][i], x);
}
void check(int x, int y) {
if (L[x] >= R[y]) {fin = 1; return ;}
int midx = (L[x] + R[x]) >> 1, midy = (L[y] + R[y]) >> 1;
if (L[x] > midy) L[y] = midy + 1, update(y);
if (midx >= R[y]) R[x] = midx, update(x);
return ;
}
int add_teleporter(int u, int v) {
u++; v++; G[u].push_back(v); G_[v].push_back(u);
check(u, v); return fin;
}
/*
#include<cstdlib>
int read_int() {
int x;
if (scanf("%d", &x) != 1) {
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
int main() {
int N = read_int();
int M = read_int();
int K = read_int();
std::vector<int> u(M), v(M);
for (int i = 0; i < M; ++i) {
u[i] = read_int();
v[i] = read_int();
}
init(N, K);
int i;
for (i = 0; i < M; ++i) {
int answer = add_teleporter(u[i], v[i]);
if (answer != 0 && answer != 1) {
i = -1;
break;
} else if (answer == 1) {
break;
}
}
printf("%d\n", i);
return 0;
}
*/