【LOJ3278】「JOISC 2020 Day3」收获

题目链接

点击打开链接

题目解法

人和树是在相对运动的,考虑固定人的位置,移动树。

可以发现,一棵树 i i 在被某个人采摘后,接下来可能采摘这棵树的人是确定的,并且,两次采摘的间隔时间也是确定的,分别记为 n x t i , l e n i nxt_i,len_i 。这样的结构构成了一个基环内向森林。

考虑一次询问,对于询问在环上的人,一棵树的贡献将会是某个数值除去此人所在环长下取整的值;对于询问不在环上的人,一棵树至多产生一次贡献,且能产生贡献当且仅当其在此人的子树内,且时间足够其来到此人处。

由此,通过预处理基环树的一系列信息,不难得到一个 O ( M × Q ) O(M\times Q) 的解法。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
int tot, belong[MAXN], dest[MAXN];
ll depth[MAXN], cor[MAXN], clen[MAXN];
int timer, dfn[MAXN], rit[MAXN];
int nxt[MAXN], len[MAXN];
int sta[MAXN], slt[MAXN];
int n, m, q, l, c, x[MAXN], y[MAXN];
vector <pair <int, int>> a[MAXN];
void dfs(int pos, int from) {
	dfn[pos] = ++timer;
	dest[pos] = from;
	for (auto x : a[pos]) {
		depth[x.first] = x.second + depth[pos];
		dfs(x.first, from);
	}
	rit[pos] = timer;
}
void init() {
	for (int i = 1; i <= n; i++) {
		int tmp = c % l;
		if (x[i] - tmp >= x[1]) {
			tmp = x[i] - tmp;
			int ql = 1, qr = i;
			while (ql < qr) {
				int mid = (ql + qr + 1) / 2;
				if (tmp >= x[mid]) ql = mid;
				else qr = mid - 1;
			}
			nxt[i] = ql, len[i] = c / l * l + x[i] - x[ql];
		} else {
			tmp = l + x[i] - tmp;
			int ql = i, qr = n;
			while (ql < qr) {
				int mid = (ql + qr + 1) / 2;
				if (tmp >= x[mid]) ql = mid;
				else qr = mid - 1;
			}
			nxt[i] = ql, len[i] = c / l * l + (x[i] - x[ql] + l);
		}
	}
	static int vis[MAXN];
	for (int i = 1; i <= n; i++)
		if (!vis[i]) {
			int pos = i;
			vis[pos] = i;
			pos = nxt[pos];
			while (!vis[pos]) {
				vis[pos] = i;
				pos = nxt[pos];
			}
			if (vis[pos] == i) {
				tot++; ll cur = 0;
				while (belong[pos] == 0) {
					belong[pos] = tot;
					cor[pos] = cur;
					cur += len[pos];
					pos = nxt[pos];
				}
				clen[tot] = cur;
			}
		}
	for (int i = 1; i <= m; i++) {
		if (y[i] < x[1]) sta[i] = n, slt[i] = l - x[n] + y[i];
		else {
			int tmp = y[i];
			int ql = 1, qr = n;
			while (ql < qr) {
				int mid = (ql + qr + 1) / 2;
				if (tmp >= x[mid]) ql = mid;
				else qr = mid - 1;
			}
			sta[i] = ql, slt[i] = y[i] - x[ql];
		}
	}
	for (int i = 1; i <= n; i++)
		if (belong[i] == 0) {
			a[nxt[i]].emplace_back(i, len[i]);
		}
	for (int i = 1; i <= n; i++)
		if (belong[i] != 0) {
			dfs(i, i);
		}
}
int main() {
	read(n), read(m), read(l), read(c);
	for (int i = 1; i <= n; i++)
		read(x[i]);
	for (int i = 1; i <= m; i++)
		read(y[i]);
	init(), read(q);
	for (int i = 1; i <= q; i++) {
		int v; ll t; read(v), read(t); ll ans = 0;
		if (belong[v] == 0) {
			for (int j = 1; j <= m; j++)
				if (dfn[v] <= dfn[sta[j]] && dfn[sta[j]] <= rit[v]) {
					ans += depth[sta[j]] - depth[v] + slt[j] <= t;
				}
		} else {
			for (int j = 1; j <= m; j++)
				if (belong[v] == belong[dest[sta[j]]] && t >= depth[sta[j]] - depth[dest[sta[j]]] + slt[j]) {
					ll tmp = t - (depth[sta[j]] - depth[dest[sta[j]]] + slt[j]);
					int pos = dest[sta[j]]; ll dist = cor[v] - cor[pos];
					if (dist < 0) dist += clen[belong[pos]];
					if (tmp >= dist) ans += 1 + (tmp - dist) / clen[belong[pos]];
				}
		}
		printf("%lld\n", ans);
	}
	return 0;
}

注意到该解法中,不在环上的人询问显然可以通过离线 + + 扫描线优化。

而在环上的人的处理方式可以改写为方便优化的形式,
具体可见下方代码的 SolveCircleBruteForce() 函数。

从而同样通过离线 + + 扫描线进行优化。

时间复杂度 O ( ( N + M + Q ) L o g N ) O((N+M+Q)LogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXP = 3e7 + 5;
const long long range = 4e18;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
struct SegmentTree {
	struct Node {
		int lc, rc;
		ll sum;
	} a[MAXP];
	int root, size;
	void init() {
		root = size = 0;
	}
	void update(int root) {
		a[root].sum = 0;
		if (a[root].lc) a[root].sum += a[a[root].lc].sum;
		if (a[root].rc) a[root].sum += a[a[root].rc].sum;
	}
	int newnode() {
		size++;
		a[size].lc = a[size].rc = 0;
		a[size].sum = 0;
		return size;
	}
	void modify(int &root, ll l, ll r, ll pos, ll val) {
		if (root == 0) root = newnode();
		int now = root;
		while (true) {
			a[now].sum += val;
			if (l == r) return;
			ll mid = (l + r) / 2;
			if (mid >= pos) {
				if (a[now].lc == 0) a[now].lc = newnode();
				now = a[now].lc, r = mid;
			} else {
				if (a[now].rc == 0) a[now].rc = newnode();
				now = a[now].rc, l = mid + 1;
			}
		}
	}
	void modify(ll pos, ll d) {
		modify(root, 0, range, pos, d);
	}
	void modify(int &root, ll pos, ll d) {
		modify(root, 0, range, pos, d);
	}
	ll query(int root, ll l, ll r, ll ql, ll qr) {
		if (root == 0) return 0;
		if (l == ql && r == qr) return a[root].sum;
		ll mid = (l + r) / 2, ans = 0;
		if (mid >= ql) ans += query(a[root].lc, l, mid, ql, min(qr, mid));
		if (mid + 1 <= qr) ans += query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		return ans;
	}
	ll query(ll l, ll r) {
		if (l > r) return 0;
		return query(root, 0, range, l, r);
	}
	ll query(int root, ll l, ll r) {
		if (l > r) return 0;
		return query(root, 0, range, l, r);
	}
} ST;
int tot, belong[MAXN], dest[MAXN];
ll depth[MAXN], cor[MAXN], clen[MAXN];
int timer, dfn[MAXN], rit[MAXN];
int nxt[MAXN], len[MAXN];
int sta[MAXN], slt[MAXN];
int n, m, q, l, c, x[MAXN], y[MAXN];
vector <pair <int, int>> a[MAXN];
void dfs(int pos, int from) {
	dfn[pos] = ++timer;
	dest[pos] = from;
	for (auto x : a[pos]) {
		depth[x.first] = x.second + depth[pos];
		dfs(x.first, from);
	}
	rit[pos] = timer;
}
void init() {
	for (int i = 1; i <= n; i++) {
		int tmp = c % l;
		if (x[i] - tmp >= x[1]) {
			tmp = x[i] - tmp;
			int ql = 1, qr = i;
			while (ql < qr) {
				int mid = (ql + qr + 1) / 2;
				if (tmp >= x[mid]) ql = mid;
				else qr = mid - 1;
			}
			nxt[i] = ql, len[i] = c / l * l + x[i] - x[ql];
		} else {
			tmp = l + x[i] - tmp;
			int ql = i, qr = n;
			while (ql < qr) {
				int mid = (ql + qr + 1) / 2;
				if (tmp >= x[mid]) ql = mid;
				else qr = mid - 1;
			}
			nxt[i] = ql, len[i] = c / l * l + (x[i] - x[ql] + l);
		}
	}
	static int vis[MAXN];
	for (int i = 1; i <= n; i++)
		if (!vis[i]) {
			int pos = i;
			vis[pos] = i;
			pos = nxt[pos];
			while (!vis[pos]) {
				vis[pos] = i;
				pos = nxt[pos];
			}
			if (vis[pos] == i) {
				tot++; ll cur = 0;
				while (belong[pos] == 0) {
					belong[pos] = tot;
					cor[pos] = cur;
					cur += len[pos];
					pos = nxt[pos];
				}
				clen[tot] = cur;
			}
		}
	for (int i = 1; i <= m; i++) {
		if (y[i] < x[1]) sta[i] = n, slt[i] = l - x[n] + y[i];
		else {
			int tmp = y[i];
			int ql = 1, qr = n;
			while (ql < qr) {
				int mid = (ql + qr + 1) / 2;
				if (tmp >= x[mid]) ql = mid;
				else qr = mid - 1;
			}
			sta[i] = ql, slt[i] = y[i] - x[ql];
		}
	}
	for (int i = 1; i <= n; i++)
		if (belong[i] == 0) {
			a[nxt[i]].emplace_back(i, len[i]);
		}
	for (int i = 1; i <= n; i++)
		if (belong[i] != 0) {
			dfs(i, i);
		}
}
int v[MAXN]; ll t[MAXN], ans[MAXN];
void SolveTree() {
	ST.init();
	static vector <pair <ll, int>> qry[MAXN], mod[MAXN];
	for (int i = 1; i <= q; i++)
		if (belong[v[i]] == 0) {
			qry[dfn[v[i]] - 1].emplace_back(t[i] + depth[v[i]], -i);
			qry[rit[v[i]]].emplace_back(t[i] + depth[v[i]], i);
		}
	for (int i = 1; i <= m; i++)
		mod[dfn[sta[i]]].emplace_back(depth[sta[i]] + slt[i], 1);
	for (int i = 1; i <= n; i++) {
		for (auto x : mod[i])
			ST.modify(x.first, x.second);
		for (auto x : qry[i])
			if (x.second < 0) ans[-x.second] -= ST.query(0, x.first);
			else ans[x.second] += ST.query(0, x.first);
	}
}
void SolveCircleBruteForce() {
	ST.init();
	static ll prelen[MAXN];
	for (int i = 1; i <= m; i++)
		prelen[i] = depth[sta[i]] - depth[dest[sta[i]]] + slt[i];
	for (int i = 1; i <= q; i++) {
		if (belong[v[i]] == 0) continue;
		for (int j = 1; j <= m; j++)
			if (belong[v[i]] == belong[dest[sta[j]]] && t[i] >= prelen[j]) {
				int pos = dest[sta[j]];
				ll x = t[i] - cor[v[i]] + clen[belong[pos]], y = prelen[j] - cor[pos] + clen[belong[pos]];
				ans[i] += x / clen[belong[pos]] + 1;
				ans[i] -= y / clen[belong[pos]];
				ans[i] -= x % clen[belong[pos]] < y % clen[belong[pos]];
				if (cor[v[i]] < cor[pos]) ans[i] -= 1;
			}
	}
}
void SolveCircle() {
	ST.init();
	static ll prelen[MAXN], cnt[MAXN], mns[MAXN];
	static int root[MAXN][2]; int tot = m;
	static pair <int, bool> p[MAXN * 2];
	for (int i = 1; i <= m; i++) {
		prelen[i] = depth[sta[i]] - depth[dest[sta[i]]] + slt[i];
		p[i] = make_pair(i, false);
	}
	for (int i = 1; i <= q; i++)
		if (belong[v[i]] != 0) p[++tot] = make_pair(i, true);
	sort(p + 1, p + tot + 1, [&] (pair <int, bool> a, pair <int, bool> b) {
		ll ta = a.second ? t[a.first] : prelen[a.first], tb = b.second ? t[b.first] : prelen[b.first];
		if (ta == tb) return a.second < b.second;
		else return ta < tb;
	});
	for (int i = 1; i <= tot; i++) {
		if (p[i].second) {
			int j = p[i].first; ll x = t[j] - cor[v[j]] + clen[belong[v[j]]];
			ans[j] += (x / clen[belong[v[j]]] + 1) * cnt[belong[v[j]]];
			ans[j] -= mns[belong[v[j]]] + ST.query(root[belong[v[j]]][0], x % clen[belong[v[j]]] + 1, range) + ST.query(root[belong[v[j]]][1], cor[v[j]] + 1, range);
		} else {
			int j = p[i].first, pos = dest[sta[j]];
			ll y = prelen[j] - cor[pos] + clen[belong[pos]];
			cnt[belong[pos]]++, mns[belong[pos]] += y / clen[belong[pos]];
			ST.modify(root[belong[pos]][0], y % clen[belong[pos]], 1);
			ST.modify(root[belong[pos]][1], cor[pos], 1);
		}
	}
}
int main() {
	read(n), read(m), read(l), read(c);
	for (int i = 1; i <= n; i++)
		read(x[i]);
	for (int i = 1; i <= m; i++)
		read(y[i]);
	init(), read(q);
	for (int i = 1; i <= q; i++)
		read(v[i]), read(t[i]);
	SolveTree(), SolveCircle();
	for (int i = 1; i <= q; i++)
		printf("%lld\n", ans[i]);
	return 0;
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/105100433