洛谷 P7830 [CCO2021] Through Another Maze Darkly

发布时间 2023-10-05 22:52:26作者: zltzlt

洛谷传送门

被联考创出 shit 了。

考虑一种极限情况:每个点指向父亲。那么这种情况我们会顺着欧拉序完整地把整棵树都走一遍。

但是初始的时候不一定每个点都指向父亲。发现我们走过 \(O(n^2)\) 步就能到达上面的极限情况。比较显然,因为每次扩展至少使一个点从不指向父亲变成指向父亲(称一次扩展为,不处于极限情况时,根结点走完一遍又回到自己的过程)。

考虑若一个点初始不指向父亲,那么第一次扩展到它时,它还没来得及扩展所有子结点就回到父亲了。但是第二次扩展可以扩展所有子结点。所以一个点至多需要访问 \(2\) 次。基于此考虑用一个队列维护还需要被访问的点,若遇到一个点没扩展完所有子结点就返回,就把它 push 进队列。

考虑如何回答询问。注意到每次扩展经过的结点编号序列,一定是最终欧拉序的子序列。于是我们把询问离线下来,扩展时把对应点的对应位置插入序列,查询就查一个下标第 \(k\) 大。可以使用树状数组维护。对于走到第 \(k\) 步时已经把整棵树扩展完的询问,因为它的周期是整棵树的欧拉序,所以也可以快速回答。

时间复杂度 \(O((n + q) \log n + q \log q)\)

code
// Problem: P7830 [CCO2021] Through Another Maze Darkly
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P7830
// Memory Limit: 1 MB
// Time Limit: 8000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 22, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 22], *p1 = buf, *p2 = buf;
inline ll read() {
    char c = getchar();
    ll x = 0;
    for (; !isdigit(c); c = getchar()) ;
    for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x;
}

const int maxn = 1600100;

ll n, m, tt, fa[maxn], fd[maxn], ans[maxn], pt[maxn], a[maxn], tot, b[maxn], cnt;
bool vis[maxn];
pii qq[maxn];
vector<pii> G[maxn];
vector<int> vc[maxn];

void dfs(int u, int f) {
	for (pii &p : G[u]) {
		int v = p.fst;
		if (v == f) {
			p.scd = fd[u];
			continue;
		}
		p.scd = ++tt;
		fa[v] = u;
		fd[v] = tt;
		dfs(v, u);
	}
}

void dfs2(int u, int fa) {
	int t = pt[u], len = (int)G[u].size();
	while (1) {
		pt[u] = (pt[u] + 1) % len;
		if (pt[u] == t && u > 1) {
			break;
		}
		int v = G[u][pt[u]].fst;
		a[++tot] = v;
		b[tot] = G[u][pt[u]].scd;
		dfs2(v, u);
		a[++tot] = u;
		b[tot] = G[u][pt[u]].scd;
		if (pt[u] == t && u == 1) {
			break;
		}
	}
}

namespace BIT {
	int c[maxn];
	
	inline void update(int x, int d) {
		++cnt;
		for (int i = x; i <= n * 2 - 2; i += (i & (-i))) {
			c[i] += d;
		}
	}
	
	inline int kth(int k) {
		int p = 0;
		for (int i = __lg(n * 2 - 2); ~i; --i) {
			if (p + (1 << i) <= n * 2 - 2 && c[p + (1 << i)] < k) {
				p += (1 << i);
				k -= c[p];
			}
		}
		return p + 1;
	}
}

vector<int> nxt;

void dfs3(int u) {
	int len = (int)G[u].size();
	while (1) {
		pt[u] = (pt[u] + 1) % len;
		int v = G[u][pt[u]].fst, id = G[u][pt[u]].scd;
		if (vis[id]) {
			break;
		}
		if (v == fa[u]) {
			nxt.pb(u);
			break;
		}
		BIT::update(vc[id][0], 1);
		dfs3(v);
		vis[id] = 1;
		BIT::update(vc[id][1], 1);
	}
}

void solve() {
	n = read();
	m = read();
	for (int i = 1, k; i <= n; ++i) {
		k = read();
		while (k--) {
			G[i].pb(read(), 0);
		}
	}
	dfs(1, -1);
	for (int i = 2; i <= n; ++i) {
		for (int j = 0; j < (int)G[i].size(); ++j) {
			if (G[i][j].fst == fa[i]) {
				pt[i] = j;
				break;
			}
		}
	}
	dfs2(1, -1);
	for (int i = 1; i <= tot; ++i) {
		vc[b[i]].pb(i);
	}
	for (int i = 1; i <= m; ++i) {
		qq[i] = mkp(read(), i);
	}
	sort(qq + 1, qq + m + 1);
	mems(pt, 0);
	ll s = 0, j = 1;
	queue<int> q;
	q.push(1);
	while (cnt < tot) {
		ll lst = s;
		vector<int>().swap(nxt);
		while (q.size()) {
			int u = q.front();
			q.pop();
			dfs3(u);
		}
		for (int x : nxt) {
			q.push(x);
		}
		s += cnt;
		while (j <= m && qq[j].fst <= s) {
			ans[qq[j].scd] = a[BIT::kth(qq[j].fst - lst)];
			++j;
		}
	}
	while (j <= m) {
		ans[qq[j].scd] = a[(qq[j].fst - s - 1) % tot + 1];
		++j;
	}
	for (int i = 1; i <= m; ++i) {
		printf("%lld\n", ans[i]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}