「JOISC 2020 Day3」收获

传送门

做了整整一天的时间了


思路

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

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

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

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

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

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

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

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

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

将式子变成 \(dis_u+t\ge dis_v+d_x\),我们就可以用树状数组来维护

对于情况二,我们先统一将果树移到基环树的根节点处,设每棵果树移动的距离为 \(T_i\),对于环上的询问 \((u,t)\),每棵果树的贡献为 \(\sum_{T_i\le t}\frac{t-T_i}{len}\)\(len\) 表示环长)

但这个舍入也是一个艺术细节,对于 \((t-T_i) \bmod len \ge len-dis_u\) ,就需要向上取整;反之向下取整(这里的 \(len-dis_u\) 表示根跨过断边走到 \(u\) 点的距离)

为了避免讨论,我们直接计算 \(\sum_{T_i\le t} \lfloor \frac{t-T_i+dis_u}{len} \rfloor\)

考虑变量分离,令 \(a=t-dis_u,~b=T_i\),那么计算 \(\lfloor \frac{a-b}{len} \rfloor\) 等价于 \(\lfloor \frac{a}{len} \rfloor - \lfloor \frac{b}{len} \rfloor-[a\bmod len <b\bmod len]\)

我们可以将询问和果树放一起,按照 \(t\)\(T\) 从小到大排序,令 \(pos\) 表示扫描了的果树数量,\(sub\) 表示已扫描的果树的 \(\lfloor \frac{T_i}{len} \rfloor\) 和,扫描一棵果树时在树状数组的 \(T_i\bmod len\) 处加一;当处理询问时,答案就是 \(\lfloor \frac{a}{len} \rfloor\times pos-sub+(pos-Tr[t-dis_u])\),其中 \(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;
}
posted @ 2022-09-09 14:38  zuytong  阅读(73)  评论(0编辑  收藏  举报