计蒜客 A1617 Hall 定理 + 尺取法 + 线段树

203 篇文章 3 订阅
201 篇文章 0 订阅
题意

传送门 计蒜客 A1617 LOVER II

题解

先不考虑区间查询。对于 b b b a a a,令其升序排序。令 c [ i ] c[i] c[i] 等于满足 a [ i ] + b [ j ] ≥ k a[i] + b[j] \geq k a[i]+b[j]k j j j 的数量。 c [ i ] c[i] c[i] 单调不减,且满足条件的 j j j a a a 的后缀。满足条件当且仅当 c [ i ] ≥ i c[i]\geq i c[i]i。可以通过 Hall 定理证明,满足上述条件时存在 a a a 的完备匹配。

对于 b b b,若 l l l 增加,满足条件的 r r r 一定不减。那么可以使用尺取法求解,同时使用可区间修改的线段树维护 c [ i ] − i c[i] - i c[i]i,若 [ 1 , n ] [1,n] [1,n] 最小值大于等于零,则满足条件。总时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include <bits/stdc++.h>
using namespace std;
constexpr int MAXN = 2E5 + 5, SZ = 1 << 19;
int T, N, M, K, Q;
int A[MAXN], B[MAXN];
int F[MAXN];

struct ST
{
    int dat[SZ], lz[SZ];
    void init(int k = 0, int l = 0, int r = N)
    {
        if (r - l == 1)
        {
            dat[k] = -(l + 1);
            return;
        }
        int m = (l + r) / 2, chl = k * 2 + 1, chr = k * 2 + 2;
        init(chl, l, m), init(chr, m, r);
        lz[k] = 0;
        dat[k] = min(dat[chl], dat[chr]);
    }
    void pushdown(int k)
    {
        if (lz[k] != 0)
        {
            int chl = k * 2 + 1, chr = k * 2 + 2;
            int x = lz[k];
            lz[k] = 0;
            lz[chl] += x, dat[chl] += x;
            lz[chr] += x, dat[chr] += x;
        }
    }
    void change(int a, int b, int x, int k = 0, int l = 0, int r = N)
    {
        if (r <= a || b <= l)
            return;
        if (a <= l && r <= b)
        {
            lz[k] += x, dat[k] += x;
            return;
        }
        pushdown(k);
        int m = (l + r) / 2, chl = k * 2 + 1, chr = k * 2 + 2;
        change(a, b, x, chl, l, m), change(a, b, x, chr, m, r);
        dat[k] = min(dat[chl], dat[chr]);
    }
} tr;

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> T;
    while (T--)
    {
        cin >> N >> M >> K;
        for (int i = 0; i < N; ++i)
            cin >> A[i];
        for (int i = 0; i < M; ++i)
            cin >> B[i];
        sort(A, A + N);
        tr.init();
        for (int l = 0, r = 0; l < M;)
        {
            while (r < M && tr.dat[0] < 0)
            {
                int pos = lower_bound(A, A + N, K - B[r]) - A;
                if (pos < N)
                    tr.change(pos, N, 1);
                ++r;
            }
            F[l] = tr.dat[0] >= 0 ? r : M + 1;
            int pos = lower_bound(A, A + N, K - B[l]) - A;
            if (pos < N)
                tr.change(pos, N, -1);
            ++l;
        }
        cin >> Q;
        while (Q--)
        {
            int l, r;
            cin >> l >> r;
            --l;
            cout << (F[l] <= r ? 1 : 0) << '\n';
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值