Y
K
N
U
F

题解:P12410 「知りたくなかった、失うのなら」

link

说在前面

如果你看了这个东西你最好就看个乐子别真的去写,卡常卡死你

做法什么的请直接看正文。

注意到其他题解给出了很优美的做法,那么我就来点不优美的。
先设几个数字吧,设 \(V\) 为值域,\(L\) 为分治的边界,\(ca\)\(a\) 大于 \(L\) 的询问数,\(cm\) 则为另一部分的询问数,再令 \(B\) 为每棵线段树的长度。
这篇题解的复杂度是这样的:

\[O(ca \sqrt n + ca \frac VL + cm (\frac V{B} + \log B) + Ln \log B) \]

上面这个东西不很好,显然不可以算。

约掉一些比较小的项,再将一些值域相等的数化为相同的后……哪怕那坨东西已经很不严谨了,我还是不会算。

总结一下,是 \(O(能过)\)

正文

首先在 \(a\) 比较大的情况下显然是可以用莫队维护区间数的出现与否然后直接枚举每一个 \(ka + b\) 做的,你飞速打了一份,觉得代码已经完成一半了。

问题只在于 \(a\) 很小的话就会变成 \(Vq\) 的复杂度,这是不可以接受的,考虑根号分治,去处理 \(a\) 小的情况。

发现对于每一个数字 \(c\)\(a\) 一定的前提下都有着唯一的 \(b + ak\) 与其对应。维护一个二维数组 \(f\)。我们可以考虑对于每一个有相同的 \(a\) 的询问按照 \(r\) 单调递增的顺序排序,每扩展一次 \(r\) 就更新 \(f_{(b,k)}\) 的值为 \(r\),这时询问就可以转化一下,分别令 \(k_x, k_y\) 为满足 \(x \le ka+b\le y\)\(k\) 的最小最大值,问题也就转化成了:
在按照 \(r\) 更新完后,查询 \(f_{(b,[k_x,k_y])}\) 的最小值是否小于 \(l\)

所以显然可以用线段树来干这件事,直接开 \(b\) 棵来维护就行了。

然后你设分治边界是 \(L\),高高兴兴的把 \(L\) 设置为 \(\sqrt n\),再然后 TLE 了。

查询总复杂度 \(O(ca \log n)\),修改总复杂度 \(O(Ln \log n)\),你发现这个东西总复杂度竟然是 \(O(\sqrt n \log n)\),烂透了,所以想要优化。

我们可以对于每一个 \(b\) 开多棵线段树,维护不同段的最小值,然后就很好,线段树整块查询是 \(O(1)\) 的,你设块长为 \(B\)

查询总复杂度 \(O(ca{(\frac nB + \log B)})\),修改总复杂度 \(O(Ln \log B)\),你发现这个东西好极了,所以设 \(B\)\(\sqrt {cm}\),想要通过。

然后又 TLE 了。

你发现 \(L\) 的值不很优秀,两边的复杂度不平衡。

然后你发现你不会平衡这个复杂度,然后你疯狂加入卡常并人工退火多次尝试修改 \(B\)\(L\) 并交了 \(114\) 发左右才 AC。

不是你写的代码

// code by 樓影沫瞬_Hz17
#include <bits/extc++.h>
using namespace std;
 
#define getc() getchar_unlocked()
#define putc(a) putchar_unlocked(a)
#define en_ putc('\n')
#define e_ putc(' ')
 
inline int in() { 
	int n = 0; char p = getc();
	while (p < '-') p = getc();
	do n = n * 10 + (p ^ 48), p = getc();
	while (isdigit(p));
	return n;
}
#define in(a) a = in()
 
using pii = pair<int, int>;
 
constexpr int N = 5e5 + 10, B = 800, L = 6, V = 5e5;	
#define mid ((l + r) >> 1) // 欸为什么 L 取到 6 最快啊?太诡异了
 
int c[N], n, m, cm, ca;
struct quer {
	int l, r, x, y, a, b, id;
} qm[N], qa[N];

unsigned short cnt[N]; // 这一句是 short 会跑得飞快但是会被 hack,但是其实 int 也能过
int pos[N];
int pl[N], pr[N];

bool ans[N];
int ls[N * 5], rs[N * 5], rt[N / 10][L], mi[N * 5];
int cntn = 0;

inline void update(int &u, const int l, const int r, const int p, const int x) {
	if(!u) u = ++ cntn;
	if(l == r) {
		mi[u] = x;
		return;
	}
	if(p <= mid) update(ls[u], l, mid, p, x);
	else update(rs[u], mid + 1, r, p, x);
	mi[u] = mi[ls[u]] < mi[rs[u]] ? mi[ls[u]] : mi[rs[u]];
}

inline int query(const int u, const int l, const int r, const int L, const int R) {
	if(!u) return -1;
	if(L <= l and r <= R) return mi[u];
	if(L <= mid and R > mid) return min(query(ls[u], l, mid, L, R), query(rs[u], mid + 1, r, L, R));
	if(L <= mid) return query(ls[u], l, mid, L, R);
	if(R > mid) return query(rs[u], mid + 1, r, L, R);
	return 500000;
}

signed main() {
	in(n), in(m);
	for(int i = 1; i <= n; i ++) in(c[i]);

	quer q;
	for(int i = 1; i <= m; i ++) {
		in(q.l), in(q.r), in(q.x);
		in(q.y), in(q.a), in(q.b);
		q.id = i;
		if(q.a >= L) qa[++ ca] = q;
		else qm[++ cm] = q;
	}

	for(int i = 1; i <= n; i ++) pos[i] = i / B;

	sort(qa + 1, qa + 1 + ca, [](const quer&a, const quer&b) {
		return pos[a.l] == pos[b.l] ? pos[a.l] & 1 ? a.r < b.r : a.r > b.r : pos[a.l] < pos[b.l];
	});

	for(int i = 1, l = 1, r = 0, L, a, b, x, y; i <= ca; i ++) {
		while(l < qa[i].l) cnt[c[l]] --, l ++;
		while(l > qa[i].l) l --, cnt[c[l]] ++;
		while(r < qa[i].r) r ++, cnt[c[r]] ++;
		while(r > qa[i].r) cnt[c[r]] --, r --;

		L, a = qa[i].a, b = qa[i].b, x = qa[i].x, y = qa[i].y;
		L = x < b ? 0 : (x - b) / a + ((x - b) % a != 0);
		ans[qa[i].id] = 1;
		for(; a * (L + 8) + b <= y; L += 8) 
			if(!cnt[a * L + b] || !cnt[a * (L + 1) + b] || !cnt[a * (L + 2) + b] || !cnt[a * (L + 3) + b] || !cnt[a * (L + 4) + b] || !cnt[a * (L + 5) + b] || !cnt[a * (L + 6) + b] || !cnt[a * (L + 7) + b]) {
				ans[qa[i].id] = 0;
				break;
			}
		for(; a * L + b <= y; L ++) 
			if(!cnt[a * L + b]) {
				ans[qa[i].id] = 0;
				break;
			}
	}

	sort(qm + 1, qm + 1 + cm, [](const quer&a, const quer&b) { return a.a == b.a ? a.r < b.r : a.a < b.a; });
	
	const int B = sqrt(cm) + 1;
	for(int j = 1; pr[j - 1] != V; j ++) {
		pl[j] = B * (j - 1), pr[j] = min(V, B * j - 1);
		for(int k = pl[j]; k <= pr[j]; k ++) pos[k] = j;
	}
	memset(mi, ~0x1f, sizeof mi);

	for(int i = 1, r, a, b, x, y, p, j, ql, qr, id; i <= cm; i ++) {
		a = qm[i].a, b = qm[i].b, x = qm[i].x, y = qm[i].y, id = qm[i].id;
		if(a != qm[i - 1].a) {
			memset(mi, ~0x1f, (cntn + 1) << 2);
			memset(ls, 0, (cntn + 1) << 2);
			memset(rs, 0, (cntn + 1) << 2);
			memset(rt, 0, sizeof rt);
			cntn = 0;
			r = 0;
		}
		while(r < qm[i].r) {
			r ++; 
			p = (c[r] - (c[r] % a)) / a; 
			update(rt[pos[p]][c[r] % a], pl[pos[p]], pr[pos[p]], p, r);	
		}
		ql = x <= b ? 0 : (x - b) / a + ((x - b) % a != 0), qr = (y - b) / a;
		const int Y = qm[i].l;
		if(pos[ql] == pos[qr]) {
			ans[id] = (query(rt[pos[ql]][b], pl[pos[ql]], pr[pos[qr]], ql, qr) >= Y);
			continue;
		}
		ans[id] = 1;
		query(rt[pos[ql]][b], pl[pos[ql]], pr[pos[ql]], ql, pr[pos[ql]]) < Y ? ans[id] = 0 : 0;
		query(rt[pos[qr]][b], pl[pos[qr]], pr[pos[qr]], pl[pos[qr]], qr) < Y ? ans[id] = 0 : 0;
		if(!ans[id]) continue;
		for(j = pos[ql] + 1; j < pos[qr]; j ++) 
			if(mi[rt[j][b]] < Y) {
				ans[id] = 0;
				break;
			}
	}

	for(int i = 1; i <= m; i ++) 
		puts(ans[i] ? "YES" : "NO");
}  
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~
posted @ 2025-10-03 17:20  樓影沫瞬_17Hz  阅读(25)  评论(2)    收藏  举报