「JOISC 2020 Day3」收获

传送门

做了整整一天的时间了


思路

一个人在某棵树摘苹果后,下一个在这棵树摘苹果的人是固定的

我们连接一条由前一个人指向后一个人的边,这样会组成一个内向基环树森林

具体而言,就是每个人指向逆时针上第一个与他的距离 C 的人

同时,我们将每棵果树放在第一个摘它的人的结点上,人移动就相当于果树顺着内向边移动

我们将询问挂到对应的结点上,每次处理一棵基环树,分两种情况讨论:

选择环上某一点为根,断开它与其父亲的连边,并定义 disu 为结点 u 到根的距离

  1. 果树的移动没有经过断边

  2. 果树的移动经过了断边(可以多次经过)

对于情况一,每个询问 (u,t) 要统计的就是:子树内满足 disvdisu+dxt 的果树个数,其中 dx 表示的是果树 x 与第一个遇到它的人 v 的距离

将式子变成 disu+tdisv+dx,我们就可以用树状数组来维护

对于情况二,我们先统一将果树移到基环树的根节点处,设每棵果树移动的距离为 Ti,对于环上的询问 (u,t),每棵果树的贡献为 TittTilenlen 表示环长)

但这个舍入也是一个艺术细节,对于 (tTi)modlenlendisu ,就需要向上取整;反之向下取整(这里的 lendisu 表示根跨过断边走到 u 点的距离)

为了避免讨论,我们直接计算 TittTi+disulen

考虑变量分离,令 a=tdisu, b=Ti,那么计算 ablen 等价于 alenblen[amodlen<bmodlen]

我们可以将询问和果树放一起,按照 tT 从小到大排序,令 pos 表示扫描了的果树数量,sub 表示已扫描的果树的 Tilen 和,扫描一棵果树时在树状数组的 Timodlen 处加一;当处理询问时,答案就是 alen×possub+(posTr[tdisu]),其中 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__

本文作者zuytong
本文链接https://www.cnblogs.com/zuytong/p/16672789.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zuytong  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示