Codeforces 338E 线段树

题意

传送门 Codeforces 338E Optimize!

题解

对于每一个 j ( 1 ≤ j ≤ n − l e n + 1 ) j(1\leq j\leq n-len+1) j(1jnlen+1),求是否存在一个次序使 a [ j + i ] = b [ i ] , 1 ≤ i ≤ l e n a[j+i] = b[i],1\leq i\leq len a[j+i]=b[i],1ilen

对于 l e n len len 长的 b b b a a a 的子数组,令其升序排序。令 c [ i ] c[i] c[i] 等于满足 b [ i ] + a [ j ] ≥ h b[i] + a[j] \geq h b[i]+a[j]h 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。有两种证明策略。

一种是贪心策略。对于 b b b,依次考虑 i i i,贪心地取可以选取的,且未被 i ′ ( i ′ < i ) i^\prime(i^\prime<i) i(i<i) 选取的 A [ j ] A[j] A[j]。若对于所有 i i i 都能找到匹配的 j j j,则有 c [ i ] ≥ i c[i]\geq i c[i]i

一种是根据 Hall 定理证明。问题等价于求完备匹配。若 b [ i ] + a [ j ] ≥ h b[i] + a[j] \geq h b[i]+a[j]h,看作两个节点之间连接了一条无向边。对任一个 b b b 的规模为 k k k 子集 S S S,当且仅当与其连边的点集 A ( S ) A(S) A(S) 规模满足 ∣ A ( S ) ∣ ≥ ∣ S ∣ \lvert A(S) \rvert \geq \lvert S\rvert A(S)S,二分图中存在完备匹配。由于 j > i j>i j>i 时, b [ j ] b[j] b[j] 连边的点集包含 b [ i ] b[i] b[i] 连边的点集,故只用考虑点集中索引最大者即可。又由于 c [ i ] c[i] c[i] 单调不减,则条件为 c [ i ] ≥ i c[i]\geq i c[i]i

f [ i ] = c [ i ] − i f[i] = c[i] - i f[i]=c[i]i,维护可区间修改的线段树。总时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include <bits/stdc++.h>
using namespace std;
constexpr int MAXN = 15E4 + 5, SZ = 1 << 19;
int N, L, H;
int A[MAXN], B[MAXN];

struct ST
{
    int dat[SZ], lz[SZ];
    void init(int k = 0, int l = 0, int r = L)
    {
        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 = L)
    {
        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 >> N >> L >> H;
    for (int i = 0; i < L; ++i)
        cin >> B[i];
    for (int i = 0; i < N; ++i)
        cin >> A[i];
    sort(B, B + L);
    tr.init();
    for (int i = 0; i < L; ++i)
    {
        int pos = lower_bound(B, B + L, H - A[i]) - B;
        if (pos < L)
            tr.change(pos, L, 1);
    }
    int res = tr.dat[0] >= 0;
    for (int i = L; i < N; ++i)
    {
        int pos = lower_bound(B, B + L, H - A[i]) - B;
        if (pos < L)
            tr.change(pos, L, 1);
        pos = lower_bound(B, B + L, H - A[i - L]) - B;
        if (pos < L)
            tr.change(pos, L, -1);
        res += tr.dat[0] >= 0;
    }
    cout << res << '\n';
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值