传送门
做了整整一天的时间了
思路
一个人在某棵树摘苹果后,下一个在这棵树摘苹果的人是固定的
我们连接一条由前一个人指向后一个人的边,这样会组成一个内向基环树森林
具体而言,就是每个人指向逆时针上第一个与他的距离 ≥C 的人
同时,我们将每棵果树放在第一个摘它的人的结点上,人移动就相当于果树顺着内向边移动
我们将询问挂到对应的结点上,每次处理一棵基环树,分两种情况讨论:
选择环上某一点为根,断开它与其父亲的连边,并定义 disu 为结点 u 到根的距离
-
果树的移动没有经过断边
-
果树的移动经过了断边(可以多次经过)
对于情况一,每个询问 (u,t) 要统计的就是:子树内满足 disv−disu+dx≤t 的果树个数,其中 dx 表示的是果树 x 与第一个遇到它的人 v 的距离
将式子变成 disu+t≥disv+dx,我们就可以用树状数组来维护
对于情况二,我们先统一将果树移到基环树的根节点处,设每棵果树移动的距离为 Ti,对于环上的询问 (u,t),每棵果树的贡献为 ∑Ti≤tt−Tilen(len 表示环长)
但这个舍入也是一个艺术细节,对于 (t−Ti)modlen≥len−disu ,就需要向上取整;反之向下取整(这里的 len−disu 表示根跨过断边走到 u 点的距离)
为了避免讨论,我们直接计算 ∑Ti≤t⌊t−Ti+disulen⌋
考虑变量分离,令 a=t−disu, b=Ti,那么计算 ⌊a−blen⌋ 等价于 ⌊alen⌋−⌊blen⌋−[amodlen<bmodlen]
我们可以将询问和果树放一起,按照 t、T 从小到大排序,令 pos 表示扫描了的果树数量,sub 表示已扫描的果树的 ⌊Tilen⌋ 和,扫描一棵果树时在树状数组的 Timodlen 处加一;当处理询问时,答案就是 ⌊alen⌋×pos−sub+(pos−Tr[t−disu]),其中 Tr[a] 表示树状数组 a 处的前缀和
有亿些细节
代码
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
inline LL reads()
{
LL sign = 1, re = 0; char c = getchar();
while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
return sign * re;
}
int n, m, L, C, qcnt, a[400005], b[200005];
int fa[200005], id[200005];
LL dis[200005], len[200005], ans[200005];
std::vector<int> r[200005], tr[200005];
std::vector<LL> ntr;
struct Node {LL ti; int id;};
std::vector<Node> q[200005], cq;
std::bitset<200005> circle;
LL s1[200005]; int cnt1;
LL s2[200005]; int cnt2;
struct BIT
{
int sum[200005], lim;
inline void Init(int x) {lim = x; memset(sum, 0, sizeof(int) * (lim + 1));}
inline int lb(int x) {return x & (-x);}
inline void add(int x, int val)
{
while(x <= lim)
sum[x] += val,
x += lb(x);
}
inline int query(int x)
{
if(x < 1) return 0;
int re = 0;
while(x)
re += sum[x],
x ^= lb(x);
return re;
}
}T1, T2;
void dfs1(int now)
{
for(int i : tr[now]) s1[++cnt1] = dis[now] + i;
for(int to : r[now])
{
if(id[to] > 0) continue;
id[to] = id[now], dis[to] += dis[now];
dfs1(to);
}
}
void dfs2(int now)
{
for(Node &i : q[now])
{
if(circle[now])
{
LL Ti = i.ti - (len[id[now]] - dis[now]);
if(Ti >= 0) cq.emplace_back((Node){i.ti + dis[now], i.id});
}
ans[i.id] -= T1.query(i.ti = std::upper_bound(s1 + 1, s1 + 1 + cnt1, i.ti + dis[now]) - s1 - 1);
}
for(int to : r[now]) if(to ^ id[now]) dfs2(to);
for(int i : tr[now]) T1.add(std::lower_bound(s1 + 1, s1 + 1 + cnt1, dis[now] + i) - s1, 1), ntr.emplace_back(dis[now] + i);
for(Node &i : q[now]) ans[i.id] += T1.query(i.ti);
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
n = reads(), m = reads(), L = reads(), C = reads();
FOR(i, 1, n) a[i] = reads(), a[i + n] = a[i] + L;
FOR(i, 1, m) b[i] = reads();
qcnt = reads();
FOR(i, 1, qcnt) q[reads()].emplace_back((Node){reads(), i});
int pos = 1, _C = C % L;
FOR(i, n + 1, n << 1)
{
while(pos < i && a[i] - a[pos + 1] >=_C) pos++;
dis[i - n] = (a[i] - a[pos]) + C - _C;
fa[i - n] = (pos - 1) % n + 1;
r[(pos - 1) % n + 1].emplace_back(i - n);
} pos = 0;
FOR(i, 1, m)
{
while(pos < (n << 1) && b[i] + L >= a[pos + 1]) pos++;
tr[(pos - 1) % n + 1].emplace_back(b[i] + L - a[pos]);
}
memset(id, -1, sizeof(int) * (n + 1));
FOR(i, 1, n) if(id[i] == -1)
{
int now = i;
while(id[now] == -1) id[now] = 0, now = fa[now];
id[now] = now, len[now] = dis[now], circle[now] = 1;
for(int pos = fa[now]; pos ^ now; pos = fa[pos])
len[now] += dis[pos], circle[pos] = 1;
dis[now] = 0, dfs1(now);
}
std::sort(s1 + 1, s1 + 1 + cnt1);
T1.Init(cnt1 = std::unique(s1 + 1, s1 + 1 + cnt1) - s1 - 1);
FOR(i, 1, n) if(id[i] == i)
{
ntr.clear(), cq.clear();
dfs2(i);
std::sort(ntr.begin(), ntr.end());
std::sort(cq.begin(), cq.end(), [&](Node a, Node b){return a.ti < b.ti;});
cnt2 = 0;
for(LL j : ntr) s2[++cnt2] = j % len[i];
std::sort(s2 + 1, s2 + 1 + cnt2);
T2.Init(cnt2 = std::unique(s2 + 1, s2 + 1 + cnt2) - s2 - 1);
pos = 0; LL sub = 0;
for(Node &j : cq)
{
while(pos < ntr.size() && pos <= j.ti)
T2.add(std::lower_bound(s2 + 1, s2 + 1 + cnt2, ntr[pos] % len[i]) - s2, 1),
sub += ntr[pos] / len[i], pos++;
ans[j.id] += j.ti / len[i] * pos - sub;
ans[j.id] -= pos - T2.query(std::upper_bound(s2 + 1, s2 + 1 + cnt2, j.ti % len[i]) - s2 - 1);
}
}
FOR(i, 1, qcnt) printf("%lld\n", ans[i]);
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】