2025/7/23 集训NOIP模拟赛
T1 排序题
首先看到绝对值先拆一下试试。
\(\left|x_i - x_j\right| \leq w_i - w_j \Rightarrow \begin{cases} w_i - x_i \geq w_j - x_j \\ w_i + x_i \geq w_j + x_j \end{cases}\)
埋下伏笔。
你直接考虑 \(x_i\) 为横坐标,\(w_i\) 为纵坐标,则一个点 \(i\) 能够连带染色的区域是一个斜边在 \(x\) 轴上的等腰直角三角形,这很不好做。
此时刚才拆的式子就用上了,只需要满足后面大括号的条件,那么令 \(x\) 坐标为 \(w_i + x_i\),纵坐标为 \(w_i - x_i\),然后需要维护的就是该点左下角的矩形。
那么贪心的想,只有右上角的点是需要我们主动去选的(右上角的点定义为最大的点使得其右上角不再有比它大的点)。那么只需要单调栈或者排序什么的做就行。
按照 \(u_i = w_i + x_i\) 从大到小,相同则 \(v_i = w_i - x_i\) 从大到小,如果存在 \(v_i > \max\limits_{j=1}^{i-1}\{v_j\}\) 说明 \((u_i,v_i)\) 为右上角点。
#include <bits/stdc++.h>
#define FILEIO
#define FASTIO
#define KamisatoAyaka return 0
#define int long long
using namespace std;
#ifdef FASTIO
char buf[1 << 23], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 23, stdin), p1 == p2) ? EOF : *p1++)
inline int read() {
int res = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
f = ch == '-' ? -1 : 1, ch = getchar();
while (isdigit(ch))
res = res * 10 + (ch ^ 48), ch = getchar();
return res * f;
}
static int ostk[33];
inline void write(int x) {
int top = 0;
if (x < 0)
x = -x, putchar('-');
do {
ostk[top++] = x % 10, x /= 10;
} while(x);
while(top)
putchar(ostk[--top] + '0');
putchar('\n');
}
#endif
constexpr int N = 500100;
int n, ans;
int sta[N], top;
struct points {
int x, y;
bool operator < (const points &s) const {
return x == s.x ? y > s.y : x > s.x;
}
} point[N];
signed main() {
#ifdef FILEIO
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
#endif
n = read();
for (int i = 1, a, b; i <= n; i ++) {
a = read(), b = read();
point[i].x = b + a, point[i].y = b - a;
}
sort(point + 1, point + n + 1);
for (int i = 1, mx = -3e18; i <= n; i ++) {
if (point[i].y > mx) {
mx = point[i].y;
ans ++;
}
}
write(ans);
KamisatoAyaka;
}
T2 预处理器
不是 \(2022\) 省选的预处理器。
题面翻译过来就是问有多少种构造数组 \(d_i\) 的方案使得其满足 \(\forall i \in [1, n], d_i \in [A_i, B_i]\) 且 \(\sum\limits_{i=1}^{n}{d_i} \in [S,T]\),且满足 \(d_i \bmod 2 = P_i\)。
如果 \(A_i \bmod 2 \neq P_i\),则可以 \(A_i \to A_i + 1\),那么最后会在 \((S,T]\) 内考虑。满足下界的话直接 \(S,T\) 都先减去 \(\sum{A_i}\) 再做,这样同时也满足了 \(\forall d_i \geq A_i\) 的限制。
然后你发现选的 \(d_i\) 奇偶性需要相同,那么区间 \([A_i,B_i]\) 内一定是两个两个选,那么记 \(C_i = \lfloor{\frac{B_i - A_i}{2}}\rfloor\),问题就转化成每个 \(i\) 最多选 \(C_i\),且 \(\sum{C_i} \in (\frac{S}{2},\frac{T}{2}]\) 的方案数。考虑前缀和思想,记 \(F(k)\) 表示满足 \(\sum{C_i} \leq k\) 的方案数,最后答案就是 \(F(\frac{T}{2}) - F(\frac{S}{2})\)。
下界限制很好做,插板法。但是上界不好做,考虑容斥。
考虑枚举超出限制的子集 \(\mathbb{S} \in \{1,2,\dots,n\}\),表示对于 \(x \in \mathbb{S}\) 该点选了 \(C_x + 1\) 个,那么剩余 \(k - \sum\limits_{x in \mathbb{S}}{(C_x + 1)}\) 个分给其余 \(n + 1\) 个。为什么是 \(n + 1\) 呢?因为最后的限制是小于等于,那么多的就扔到虚拟的第 \(n+1\) 个,即原本是 \(\sum\limits_{i=1}^{d_i} \leq k \to \sum\limits_{i=1}^{n+1}{d_i} = k \enspace (d_i \geq 0)\),然后又可以变成 \(\sum\limits_{i=1}^{n}{(d_i + 1)} = k + n\),这样方案数就是 \(C_n^{k+n}\),乘上容斥系数计算即可,复杂度 \(O(n^22^n)\)。
#include <bits/stdc++.h>
#define FASTIO
#define FILEIO
#define int long long
using namespace std;
#ifdef FASTIO
static int ostk[33];
char buf[1 << 23], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 23, stdin), p1 == p2) ? EOF : *p1++)
inline int read() {
int res = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
f = ch == '-' ? -1 : 1, ch = getchar();
while (isdigit(ch))
res = res * 10 + (ch ^ 48), ch = getchar();
return res * f;
}
inline void write(int x) {
int top = 0;
if (x < 0)
x = -x, putchar('-');
do {
ostk[top++] = x % 10, x /= 10;
} while(x);
while(top)
putchar(ostk[--top] + '0');
putchar('\n');
}
#endif
constexpr int N = 15;
int n, mod, S, T, sum;
int a[N], b[N], c[N];
int gcd(int x, int y) {
return !y ? x : gcd(y, x % y);
}
inline int calc(int x) {
if (x < 0)
return 0;
int res = 1;
vector<int> num;
for (int i = 1; i <= n; i ++)
num.push_back(x + i);
for (int i = 1; i <= n; i ++) {
int t = i;
for (int &j : num) {
int g = gcd(t, j);
t /= g, j /= g;
}
}
for (int j : num)
res = res * j % mod;
return res;
}
int F(int k) {
if (k < 0)
return 0;
int res = 0;
for (int s = 0; s < (1 << n); s ++) {
int val = k, popcount = 0;
for (int i = 1; i <= n; i ++) {
if ((s >> (i - 1)) & 1) {
val -= (c[i] + 1);
popcount ++;
}
}
res = (res + calc(val) * ((popcount & 1) ? -1 : 1) % mod + mod) % mod;
}
return res;
}
signed main() {
#ifdef FILEIO
freopen("preprocessor.in", "r", stdin);
freopen("preprocessor.out", "w", stdout);
#endif
n = read();
S = read();
T = read();
mod = read();
for (int i = 1, p; i <= n; i ++) {
a[i] = read();
b[i] = read();
p = read();
a[i] += ((a[i] & 1) != p);
sum += a[i];
c[i] = (b[i] - a[i]) / 2;
}
S = floor(1.0 * (S - sum - 1) / 2);
T = floor(1.0 * (T - sum) / 2);
write((F(T) - F(S) + mod) % mod);
return 0;
}
T3 计数
记 \(b_i\) 表示 \(a_i\) 的上界,即所有覆盖 \(a_i\) 的区间 \([l_j,r_j]\) 中 \(m_j\) 的最小值,我们将 \(b\) 单独拿出来考虑,在离散化过后会发现限制条件转换成了:
令 \(n'\) 为新的范围。
考虑 dp,记 \(f_i\) 表示考虑了前 \(i\) 个数,钦定第 \(i\) 个数为限制的 \(m_k'\),且满足了前缀所有区间限制的方案数。那么转移一定是从上一个最靠近 \(i\) 的区间左端点转移,则有:
\(len\) 和 \(slen\) 是指由于 \(n \leq 9 \times 10^8\),所以我们需要分段考虑,\(slen\) 则是 \(len\) 的前缀和。用线段树区间乘区间和维护转移。
离散化后预处理出 \(b\),对于覆盖 \(b\) 的每个区间求解,最后答案在 \(f_{n' + 1}\) 取到,乘起来即可。
#include <bits/stdc++.h>
#define FILEIO
#define FASTIO
#define int long long
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
#ifdef FASTIO
static int ostk[33];
char buf[1 << 23], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 23, stdin), p1 == p2) ? EOF : *p1++)
inline int read() {
int res = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
f = ch == '-' ? -1 : 1, ch = getchar();
while (isdigit(ch))
res = res * 10 + (ch ^ 48), ch = getchar();
return res * f;
}
inline void write(int x) {
int top = 0;
if (x < 0)
x = -x, putchar('-');
do {
ostk[top++] = x % 10, x /= 10;
} while(x);
while(top)
putchar(ostk[--top] + '0');
putchar('\n');
}
#endif
constexpr int N = 2010;
constexpr int mod = 998244353;
constexpr int inf = 0x3f3f3f3f3f3f3f3f;
int Test;
int n, Q, A;
int s[N], sx[N], cnts, nx;
int leaf[N], ans, f[N];
vector<int> q[N], v[N];
struct require {
int l, r, x;
} a[N];
inline int fpow(int x, int exp) {
if (!exp)
return 1;
int res = 1;
for (; exp; exp /= 2) {
if (exp & 1)
res = res * x % mod;
x = x * x % mod;
}
return res;
}
inline int length(int p) {
return (p & 1) ? (s[p / 2 + 1] - s[p / 2] - 1) : 1;
}
inline bool cmp(int x, int y) {
return a[x].r == a[y].r ? a[x].l > a[y].l : a[x].r < a[y].r;
}
namespace sgta {
struct segmentree {
int sum, cov, mul;
segmentree() {
sum = 0, cov = -1, mul = 1;
}
} sgt[N << 2];
void build(int rt, int l, int r) {
sgt[rt] = segmentree();
if (l == r)
return;
int mid = (l + r) / 2;
build(lson, l, mid);
build(rson, mid + 1, r);
}
inline void pushdown(int rt) {
if (~sgt[rt].cov) {
sgt[lson].cov = sgt[lson].sum = sgt[rt].cov;
sgt[rson].cov = sgt[rson].sum = sgt[rt].cov;
sgt[rt].cov = -1;
}
if (sgt[rt].mul > 1) {
sgt[lson].mul = sgt[rt].mul * sgt[lson].mul % mod;
sgt[lson].sum = sgt[rt].mul * sgt[lson].sum % mod;
sgt[rson].mul = sgt[rt].mul * sgt[rson].mul % mod;
sgt[rson].sum = sgt[rt].mul * sgt[rson].sum % mod;
sgt[rt].mul = 1;
}
}
inline void pushup(int rt) {
sgt[rt].sum = (sgt[lson].sum + sgt[rson].sum) % mod;
}
void update(int rt, int ql, int qr, int l, int r, int val, bool sign) {
if (ql > qr)
return;
if (ql <= l && qr >= r) {
if (sign) {
sgt[rt].mul = 1;
sgt[rt].cov = sgt[rt].sum = val;
}
else {
sgt[rt].sum = sgt[rt].sum * val % mod;
sgt[rt].mul = sgt[rt].mul * val % mod;
}
return;
}
pushdown(rt);
int mid = (l + r) / 2;
if (ql <= mid)
update(lson, ql, qr, l, mid, val, sign);
if (qr > mid)
update(rson, ql, qr, mid + 1, r, val, sign);
pushup(rt);
}
int query(int rt, int ql, int qr, int l, int r) {
if (ql > qr)
return 0;
if (ql <= l && qr >= r)
return sgt[rt].sum;
pushdown(rt);
int mid = (l + r) / 2, res = 0;
if (ql <= mid)
res = (res + query(lson, ql, qr, l, mid)) % mod;
if (qr > mid)
res = (res + query(rson, ql, qr, mid + 1, r)) % mod;
return res;
}
}
namespace sgtb {
struct segmentree {
int minn, tag;
segmentree() {
tag = inf;
}
} sgt[N << 2];
void build(int rt, int l, int r) {
sgt[rt] = segmentree();
if (l == r)
return;
int mid = (l + r) / 2;
build(lson, l, mid);
build(rson, mid + 1, r);
}
void cover(int rt, int ql, int qr, int l, int r, int val) {
if (ql > qr)
return;
if (ql <= l && qr >= r) {
sgt[rt].tag = min(sgt[rt].tag, val);
return;
}
int mid = (l + r) / 2;
if (ql <= mid)
cover(lson, ql, qr, l, mid, val);
if (qr > mid)
cover(rson, ql, qr, mid + 1, r, val);
}
void down(int rt, int l, int r, int val) {
val = min(val, sgt[rt].tag);
if (l == r) {
leaf[l] = val;
sgt[rt].minn = length(l) ? val : 0;
return;
}
int mid = (l + r) / 2;
down(lson, l, mid, val);
down(rson, mid + 1, r, val);
sgt[rt].minn = max(sgt[lson].minn, sgt[rson].minn);
}
int query(int rt, int ql, int qr, int l, int r) {
if (ql > qr)
return inf;
if (ql <= l && qr >= r)
return sgt[rt].minn;
int mid = (l + r) / 2, res = ~inf;
if (ql <= mid)
res = max(res, query(lson, ql, qr, l, mid));
if (qr > mid)
res = max(res, query(rson, ql, qr, mid + 1, r));
return res;
}
}
void init() {
cnts = nx = 0, ans = 1;
memset(leaf, 0, sizeof(leaf));
}
void solve() {
n = read();
Q = read();
A = read();
init();
for (int i = 1; i <= Q; i ++) {
s[++cnts] = a[i].l = read();
s[++cnts] = a[i].r = read();
sx[++nx] = a[i].x = read();
}
sort(s + 1, s + cnts + 1);
cnts = unique(s + 1, s + cnts + 1) - (s + 1);
sort(sx + 1, sx + nx + 1);
nx = unique(sx + 1, sx + nx + 1) - (sx + 1);
s[cnts + 1] = n + 1;
n = cnts << 1 | 1;
sgtb::build(1, 1, n);
for (int i = 1; i <= nx; i ++)
q[i].clear(), v[i].clear();
for (int i = 1; i <= Q; i ++) {
a[i].l = (lower_bound(s + 1, s + cnts + 1, a[i].l) - s) << 1;
a[i].r = (lower_bound(s + 1, s + cnts + 1, a[i].r) - s) << 1;
a[i].x = lower_bound(sx + 1, sx + nx + 1, a[i].x) - sx;
q[a[i].x].push_back(i);
sgtb::cover(1, a[i].l, a[i].r, 1, n, a[i].x);
}
sgtb::down(1, 1, n, inf);
for (int i = 1; i <= Q; i ++) {
bool flag = false;
flag |= (sgtb::query(1, a[i].l, a[i].r, 1, n) == a[i].x);
if (!flag) {
ans = 0;
write(ans);
return;
}
}
for (int i = 1; i <= n; i ++) {
if (leaf[i] < inf && length(i))
v[leaf[i]].push_back(i);
else if (length(i))
ans = ans * fpow(A, length(i)) % mod;
}
for (int t = 1; t <= nx; t ++) {
int nf = v[t].size(), p = -1;
memset(f, 0, sizeof(int) * (nf + 5));
sort(q[t].begin(), q[t].end(), cmp);
f[0] = 1;
sgta::build(1, 1, nf);
for (int i = 1; i <= nf; i ++) {
int len = length(v[t][i - 1]);
int T = f[i - 1] * ((fpow(sx[t], len) - fpow(sx[t] - 1, len) + mod) % mod) % mod;
int nxt = i != nf ? v[t][i] : inf, maxl = 0;
while (p < (int)q[t].size() - 1 && a[q[t][p + 1]].r < nxt)
++p, maxl = max(maxl, a[q[t][p]].l);
if (maxl) {
auto it = lower_bound(v[t].begin(), v[t].end(), maxl);
maxl = it - v[t].begin() + 1;
}
if (!maxl) {
f[i] = f[i - 1] * fpow(sx[t], len) % mod;
}
else {
f[i] = (T + fpow(sx[t] - 1, len) * sgta::query(1, maxl, i - 1, 1, nf) % mod) % mod;
sgta::update(1, 1, maxl - 1, 1, nf, 0, 1);
}
sgta::update(1, i, i, 1, nf, T, 1);
sgta::update(1, 1, i - 1, 1, nf, fpow(sx[t] - 1, len), 0);
}
ans = ans * f[nf] % mod;
}
write(ans);
return;
}
signed main() {
#ifdef FILEIO
freopen("count.in", "r", stdin);
freopen("count.out", "w", stdout);
#endif
Test = read();
while (Test--)
solve();
return 0;
}

浙公网安备 33010602011771号