省选联考 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 推箱子
这不是基本一致吗?
由于 \(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;
}
未完待续......
图
嘿嘿,没有~

浙公网安备 33010602011771号