Luogu P11203 [JOIG 2024] 感染シミュレーション / Infection Simulation 题解 [ 紫 ] [ 二维偏序 ] [ 并查集 ] [ 倍增 ] [ 贪心 ]

Infection Simulation:JOI 唯一好的地方就在于样例强的一批,过了样例基本上就把题过了。

观察题目,可以发现以下性质:

  • 感染者存在的时刻一定构成一个区间。因为不可能出现感染者消失后再出现的情况。
  • \(x\) 逐渐变小的时候,被感染的人一定不会变少,感染的限制条件一定不会更紧,感染者构成的区间一定不会更短
  • 在拓展感染的右端点的时候,不一定要选择 \(R\) 最大的人进行拓展,而是要选择 \(X\) 最大的人进行拓展。因为不需要最小化拓展右端点的次数,而选择 \(X\) 最大则让感染的限制条件尽可能松。

因此,我们可以对于当前感染者的右端点 \(R\) 找出 \(\min_{1 \le i \le n, R_i \ge R} L_i\),这样就能在拓展 \(R\) 的前提下最大化 \(X\)。假设本次拓展的代价为 \(R - \min_{1 \le i \le n, R_i \ge R} L_i\)。容易发现这样拓展形成的路径是唯一的,所以在对每个询问进行处理的时候可以直接进行倍增,遇到第一个代价小于 \(X_i\) 的位置就停下,即可求出右端点的位置。感染者的左端点显然就是 \(L_{P_i}\)。或者这个也可以按 \(X_i\) 排序后,把询问离线下来,用并查集维护这坨东西。

在求出感染区间后,剩下的就是容易的了。一个人想要被感染,必须同时满足下面两个条件:

  • \(R-L + 1\ge X_i\)
  • \(L'+X_i \le R \le R'\)

其中,\(L', R'\) 表示感染者构成的时刻区间的左右端点。

这个就是个很显然的二维偏序了。直接按查询的 \(X_i\) 排序之后,双指针加树状数组求出满足条件的 \(R\) 的数量即可。

时间复杂度 \(O(n\log n)\)。注意需要离散化,并且特判第一个被感染的人 \(R_{P_i}-L_{P_i}+1 < X_i\) 的情况。

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
using pii = pair<pi, int>;
const int N = 200005, inf = 0x3f3f3f3f;
int n, q, lsh[N], cnt, idx, quep[N], quex[N], oril[N], orir[N];
struct Segment{
    int l, r;
}sg[N];
struct Edge{
    int u, v, w;
}e[N];
bool cmp1(Segment x, Segment y) { return (x.r < y.r); }
bool cmp2(Edge x, Edge y) { return (x.w > y.w); }
bool cmp3(pii x, pii y) { return (x.fi.fi > y.fi.fi); }
bool cmp4(Segment x, Segment y) { return (x.r - x.l > y.r - y.l); }
bool cmp5(int x, int y) { return (quex[x] > quex[y]); }
int fa[N];
void init()
{
    for(int i = 1; i <= cnt; i++)
        fa[i] = i;
}
int findf(int x)
{
    if(fa[x] != x) fa[x] = findf(fa[x]);
    return fa[x];
}
void combine(int x, int y)
{
    int fx = findf(x), fy = findf(y);
    if(fx == fy) return;
    fa[fx] = fy;
}
int getrk(int x)
{
    return (lower_bound(lsh + 1, lsh + cnt + 1, x) - lsh);
}
int ansrx[N], ans[N];
pii que1[N];
int que2[N];
int lowbit(int x)
{
    return (x & (-x));
}
struct BIT{
    int tr[N];
    void update(int p, ll v)
    {
        while(p <= cnt)
        {
            tr[p] += v;
            p += lowbit(p);
        }
    }
    ll query(int p)
    {
        ll res = 0;
        while(p)
        {
            res += tr[p];
            p -= lowbit(p);
        }
        return res;
    }
}tr1;
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> sg[i].l >> sg[i].r;
        lsh[++cnt] = oril[i] = sg[i].l;
        lsh[++cnt] = orir[i] = sg[i].r;
    }
    lsh[++cnt] = inf;
    lsh[++cnt] = -inf;
    sort(lsh + 1, lsh + cnt + 1);
    cnt = (unique(lsh + 1, lsh + cnt + 1) - lsh) - 1;
    sort(sg + 1, sg + n + 1, cmp1);
    int mn = -1, p = n;
    for(int i = cnt; i >= 2; i--)
    {
        if(mn != -1 && lsh[i] - sg[mn].l >= 1)
            e[++idx] = {i, getrk(sg[mn].r), lsh[i] - sg[mn].l};
        while(p >= 1 && sg[p].r >= lsh[i])
        {
            if(mn == -1 || sg[p].l < sg[mn].l) mn = p;
            p--;
        }
    }
    sort(e + 1, e + idx + 1, cmp2);
    init();
    cin >> q;
    for(int i = 1; i <= q; i++)
    {
        cin >> quep[i] >> quex[i];
        que1[i] = {{quex[i], quep[i]}, i};
    }
    sort(que1 + 1, que1 + q + 1, cmp3);
    p = 1;
    for(int i = 1; i <= q; i++)
    {
        while(p <= idx && e[p].w >= que1[i].fi.fi)
        {
            combine(e[p].u, e[p].v);
            p++;
        }
        ansrx[que1[i].se] = lsh[findf(getrk(orir[que1[i].fi.se]))];
    }
    sort(sg + 1, sg + n + 1, cmp4);
    int cntq = 0;
    for(int i = 1; i <= q; i++)
    {
        if(orir[quep[i]] - oril[quep[i]] < quex[i])
        {
            ans[i] = 1;
            continue;
        }
        que2[++cntq] = i;
    }
    sort(que2 + 1, que2 + cntq + 1, cmp5);
    p = 1;
    for(int i = 1; i <= cntq; i++)
    {
        int id = que2[i];
        while(p <= n && sg[p].r - sg[p].l >= quex[id])
        {
            tr1.update(getrk(sg[p].r), 1);
            p++;
        }
        int v = (upper_bound(lsh + 1, lsh + cnt + 1, ansrx[id]) - lsh - 1);
        int k = getrk(oril[quep[id]] + quex[id]) - 1;
        ans[id] = tr1.query(v) - tr1.query(k);
    }
    for(int i = 1; i <= q; i++)
        cout << ans[i] << "\n";
    return 0;
}
posted @ 2025-09-03 00:26  KS_Fszha  阅读(6)  评论(0)    收藏  举报