栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【luogu P8375】游戏(思维)(线段树)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【luogu P8375】游戏(思维)(线段树)

游戏 题目链接:luogu P8375 题目大意

给你 n 个点排成一列,前面 k 个是特殊点,而且每个特殊点会连一条有向边向下一个特殊点(如果下一个不是特殊点就不连)。
然后每次会加一条有向边,然后问你在什么时候开始出现了一个环包含了特殊点。

思路

首先要知道普通图加边动态判环好像是 NP 还是 NP-hard 的,所以我们考虑它有的特殊性质。
也就是前面的一段边。

那我们发现只要前面的那些特殊点能从 i i i 走到 j j j 而且 i ⩾ j igeqslant j i⩾j 即可。
那我们考虑对于每个点维护 L i , R i L_i,R_i Li​,Ri​ 分别表示到能到它的编号最大的特殊点,和它能到的编号最小的特殊点。
那不难看出如果你在加边 x → y xrightarrow y x→y 的时候发现 L x ⩾ R y L_xgeqslant R_y Lx​⩾Ry​ 那就找到环了。
(只用看这个位置是因为如果别的地方满足了它之前一定没有满足,那它一定是走这里满足的,所以这里一定也是会满足的)

那问题就是你要怎么维护,通过势能分析我们会发现如果硬要维护每个位置的 L , R L,R L,R 一定是不行的。
那我们考虑怎么维护,其实我们会发现上面那个条件可以看做是自己的区间是否是空的,有这么一个想法就是因为你只需要保证这个区间空或否即可。
那就有一个想法就是你给这个区间弄一棵线段树之类的(不用真的建出来),然后每次只有当更新的值超过了中间线,你才更新左右端点。
那这样每个点的区间弄 log ⁡ n log n logn 次,那每次都修改至多 n log ⁡ n nlog n nlogn 也就可以通过了。

代码
#include
#include

using namespace std;

const int N = 3e5 + 100;
int n, k, L[N], R[N];
//L[i] 到它的最大   R[i] 能到的最小 
vector  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;
}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/971402.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号