题目链接
题目解法
人和树是在相对运动的,考虑固定人的位置,移动树。
可以发现,一棵树 在被某个人采摘后,接下来可能采摘这棵树的人是确定的,并且,两次采摘的间隔时间也是确定的,分别记为 。这样的结构构成了一个基环内向森林。
考虑一次询问,对于询问在环上的人,一棵树的贡献将会是某个数值除去此人所在环长下取整的值;对于询问不在环上的人,一棵树至多产生一次贡献,且能产生贡献当且仅当其在此人的子树内,且时间足够其来到此人处。
由此,通过预处理基环树的一系列信息,不难得到一个 的解法。
#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() 函数。
从而同样通过离线 扫描线进行优化。
时间复杂度 。
#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;
}