省选联考 2025 题解

省选联考 2025 题解

本蒟蒻并没有参加 2025 省选 QWQ

D1T1 幸运数字

刚开题时发现根本不会,还以为是像去年一样的推式子题。

但仔细想了想,突然发现尽管直接求方案数不方便,但是求一个数是否是合法的似乎比较简单一点。

所以限制特殊性质 A 似乎就能做了。

那所以怎么判断一个数是否合法呢?贪心

设这个数是 \(x\)

对于一个序列来说,肯定是 \(x\) 越多越好的。

所以自然可以以值域将所有的二元组分为——包含 \(x\)、一定小于 \(x\)、一定大于 \(x\),可以用二分轻松获得。

先假设一个全是 \(x\) 的序列,则向里面插入一个小于 \(x\) 的数时,中位数指针向左偏一点,反之向右偏。

所以我们要让左右尽可能的相等,且让 \(x\) 尽量多。

最后设 \(cl\) 为小于的个数,\(cr\) 为大于的个数,\(s\) 为总共的个数。

这个数合法的充要条件便是 \(cl + 1 \le \left\lfloor \frac{s+1}{2} \right\rfloor \le s-cr\)

既然已经会判断了,最后来考虑怎么枚举数。

将所有的端点记录一下,当这一段没有端点时,肯定是不会发生任何变化的,所以我们判断合法只需要对于出现了变化的点判断,也就是每个左右端点。

实现使用二分可以做到 \(O(Tn\log n)\)

注:清空数组,大样例 n 单调不降

code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

#define fi first
#define se second
#define emp emplace_back
#define pb push_back
#define lid id << 1
#define rid id << 1 | 1
#define endl '\n'
using llt = long long;

const int N = 1e6 + 100;

#define int long long
using pii = pair <int, int>;
const int inf = 0x3f3f3f3f3f3f3f3f;

pii a[N], b[N], pre[N], suf[N], sum;
pair <int, pii> l[N], r[N];
int pos[N];
int n, cnt;

signed main()
{
    // freopen("data.in", "r", stdin); freopen("data.out", "w", stdout);
    ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);

    int c, T; cin >> c >> T;
    while (T--)
    {
        sum.fi = sum.se = 0;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i].fi >> a[i].se >> b[i].fi >> b[i].se;
            pos[i] = b[i].fi, pos[i + n] = b[i].se + 1;
            sum.fi += a[i].fi, sum.se += a[i].se;
            l[i] = {b[i].fi, {a[i].fi, a[i].se}};
            r[i] = {b[i].se, {a[i].fi, a[i].se}};
        }
        sort(l + 1, l + 1 + n), sort(r + 1, r + 1 + n);
        for (int i = 1; i <= n + 1; i++)
            r[i].se.fi += r[i - 1].se.fi, r[i].se.se += r[i - 1].se.se;
        l[n + 2].se = {0, 0};
        for (int i = n + 1; i >= 0; i--)
            l[i].se.fi += l[i + 1].se.fi, l[i].se.se += l[i + 1].se.se;
        sort(pos + 1, pos + 1 + n * 2);
        cnt = unique(pos + 1, pos + 1 + n * 2) - (pos + 1);
        int ans = 0;
        for (int i = 1; i < cnt; i++)
        {
            int ok = 0;
            pair <int, pii> qwq = {pos[i], {inf, inf}}, awa = {pos[i] - 1, {inf, inf}};
            int posl = lower_bound(l + 1, l + 1 + n, qwq) - l, posr = lower_bound(r + 1, r + 1 + n, awa) - r;
            if (r[posr].fi >= pos[i]) posr--;
            pii sl = l[posl].se, sr = r[posr].se, tmpl = sl, tmpr = sr;
            if (sum.se - sl.se - sr.se == 0) continue;
            if (tmpl.fi > tmpr.fi) swap(tmpl, tmpr);
            if (tmpr.fi <= tmpl.se) ok = 1;
            else
            {
                int cl = tmpl.se, cr = tmpr.fi;
                swap(cl, cr);
                if (sl.fi > sr.fi) swap(cl, cr);
                int s = sum.se - sl.se - sr.se + cl + cr;
                if (cl < (s + 1) / 2 && (s + 1) / 2 < s - cr + 1) ok = 1;
                else ok = 0;
            }
            ans += ok * (pos[i + 1] - pos[i]);
        }
        for (int i = 1; i <= n + 1; i++)
            r[i].se.fi = 0, r[i].se.se = 0;
        for (int i = n + 1; i >= 0; i--)
            l[i].se.fi = 0, l[i].se.se = 0;
        cout << ans << '\n';
    }

    return 0;
}

D2T1 推箱子

abc371_f

这不是基本一致吗?

由于 \(a, b\) 都单调递增,所以不会互相蹩脚(一个点到达目的地却不得不被移动)

只需要考虑移动的时间,那不就是原题吗?

只需将询问按时间排序,每次判断是否超时即可。

怎么求?将箱子推走后箱子是连续的位置,直接赋值太过反人类了,注意到求答案是差分的形式,所以可以直接让位置减去连续的数字,这样连续的位置的数值就是一样的了。

也就是: $$ a_i = a_i - i, b_i = b_i - i $$

所以只需要进行简单的区间赋值 + 线段树二分就行。

code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

#define fi first
#define se second
#define emp emplace_back
#define pb push_back
#define lid id << 1
#define rid id << 1 | 1
#define endl '\n'
#define IL inline
using llt = long long;
using pii = pair <int, int>;

const int N = 2e5 + 100;

#define int long long

struct Node
{
    int id, t, l, r;
}task[N];

class Sem_Tree
{
public :
    int sum[N << 2], ll[N << 2], rr[N << 2], tag[N << 2];

    IL void PushUp(int id)
    {
        sum[id] = sum[lid] + sum[rid];
        ll[id] = ll[lid], rr[id] = rr[rid];
    }

    IL void PushDown(int id, int l, int r)
    {
        int mid = (l + r) >> 1;
        if (tag[id])
        {
            tag[lid] = tag[id], tag[rid] = tag[id];
            sum[lid] = (mid - l + 1) * tag[id], sum[rid] = (r - mid) * tag[id];
            ll[lid] = ll[rid] = rr[lid] = rr[rid] = tag[id];
            tag[id] = 0;
        }
    }

    void Build(int id, int l, int r)
    {
        tag[id] = 0;
        if (l == r) return (sum[id] = ll[id] = rr[id] = task[l].l), void();
        int mid = (l + r) >> 1;
        Build(lid, l, mid); Build(rid, mid + 1, r);
        PushUp(id);
    }

    int Query_l(int id, int l, int r, int pos)
    {
        if (l == r) return ((rr[id] < pos) ? l + 1 : l);
        int mid = (l + r) >> 1;
        PushDown(id, l, r);
        if (ll[rid] > pos) return Query_l(lid, l, mid, pos);
        else return Query_l(rid, mid + 1, r, pos);
    }

    int Query_r(int id, int l, int r, int pos) // pos = b[i] - a[i]
    {
        if (l == r) return ((rr[id] > pos) ? l - 1 : l);
        int mid = (l + r) >> 1;
        PushDown(id, l, r);
        if (rr[lid] <= pos) return Query_r(rid, mid + 1, r, pos);
        else return Query_r(lid, l, mid, pos);
    }

    int Query(int id, int l, int r, int pos)
    {
        if (l == r) return sum[id];
        int mid = (l + r) >> 1;
        if (pos <= mid) return Query(lid, l, mid, pos);
        else return Query(rid, mid + 1, r, pos);
    }

    void Update(int id, int cl, int cr, int l, int r, int val)
    {
        if (l <= cl && cr <= r) return (tag[id] = ll[id] = rr[id] = val, sum[id] = (cr - cl + 1) * val), void();
        PushDown(id, cl, cr);
        int mid = (cl + cr) >> 1;
        if (l <= mid) Update(lid, cl, mid, l, r, val);
        if (r > mid) Update(rid, mid + 1, cr, l, r, val);
        PushUp(id);
    }
}T;

void solve()
{
    int n; cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> task[i].l >> task[i].r >> task[i].t;
        task[i].l -= i, task[i].r -= i;
        task[i].id = i;
    }
    T.Build(1, 1, n);
    sort(task + 1, task + 1 + n, [](Node x, Node y)
    {
        return x.t < y.t;
    });
    int su = 0;
    for (int i = 1; i <= n; i++)
    {
        int l = task[i].id, r = 0, s = T.sum[1], sl = T.Query(1, 1, n, l);
        if (sl == task[i].r) continue;
        if (sl < task[i].r) r = T.Query_r(1, 1, n, task[i].r);
        else r = T.Query_l(1, 1, n, task[i].r);
        if (l > r) swap(l, r);
        T.Update(1, 1, n, l, r, task[i].r);
        su += abs(s - T.sum[1]);
        if (su > task[i].t)
        {
            cout << "No\n";
            return;
        }
    }
    cout << "Yes\n";
}

signed main()
{
    // freopen("data.in", "r", stdin); freopen("data.out", "w", stdout);
    ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);

    int c, S; cin >> c >> S;
    while (S--)
    {
        solve();
    }
    return 0;
}

未完待续......

嘿嘿,没有~

posted @ 2025-03-22 08:49  QEDQEDQED  阅读(125)  评论(4)    收藏  举报