P5070 [Ynoi2015] 即便看不到未来

[Ynoi2015] 即便看不到未来

在太阳西斜的这个世界里,置身天上之森。等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去、逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。

————世界上最幸福的女孩 珂朵莉


思路

扫描线。

发现 \(k\leq 10\),也就是意味着 \(k\) 就是一个大常数,那我们就可以对每个 \(k\) 分开考虑。

我们考虑扫描线经典套路就是先把问题 \(l,r\) 离线并看成一个点 \((l,r)\),枚举每个枚举右端点并把左端点分段,这样的贡献可以当成矩形,原问题变成二维数点。

这道题也是同理,我们枚举右端点 \(r\) ,并且钦定当前位置就是我扫描线矩阵 \(x\) 轴的右端点,设 \([lv,rv]\) 是现在左端点到右端点中包括右端点的极长值域连续段的值域区间,那么当前序列上的区间就不能包括 \(lv-1,rv+1\) 这样的左端点的区间应该在 \((\max(pre_{lv-1},pre_{rv+1}),\min(pre_{lv},pre_{rv})]\) 之间,(其中 \(pre_v\) 表示值 \(v\) 上一次出现的位置,这个可以 \(\mathcal O(1)\) 维护)。

考虑每次 \(k\leftarrow k+1\) 的过程,实际上就是把 \([lv,rv]\) 变成 \([lv-1,rv]\) 或者 \([lv,rv+1]\) 的过程,而扩展前者还是后者则取决于 \(pre_{lv-1}\)\(pre_{rv+1}\) 的大小关系,我们必定会拓展到 \(pre_{lv-1},pre_{rv+1}\) 更大的位置,因为如果反之那么就会同时扩展两个数,不满足 \(k\leftarrow k+1\) 的条件。

最后我们考虑去重的问题,这并不是简单的矩形面积并的问题,因为一个区间 \(l,r\) 内可能包括很多长度相等的值域连续段,其实去重问题也很好解决,我们只需要关心左端点区间是不是严格 \(\leq pre_{a_r}\) 即可,因为前面的矩形一定会在右端点枚举到 \(pre_{a_r}\) 的时候加入扫描线,此时重复加入必然带来过多的贡献。

\(y\) 轴上的矩形边的处理就可以随意点了,因为 \(y\) 轴上左端点必然是 \(r\) ,而右端点则是 \(\min(nxt_{lv-1},nxt_{rv+1})\),可以用 vector + 指针达到 \(\mathcal O(1)\) 处理

最后做 \(k\) 次二维数点即可,问题转化为区间+单点求值,建议用常数小的树状数组。

时间复杂度 \(\mathcal O((n+m)k\log n)\),卡卡就能过。


code

#include <bits/stdc++.h>
#define pb push_back
using namespace std; 
const int N = 1e6 + 10;
const int K = 10;
const int INF = 0x3f3f3f3f;
typedef pair <int, int> pii;
inline int read ()
{
    int x = 0, f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
    while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
    return x * f;
}
int n, m;
int a[N];
struct Query {
	int x, y, id;
	Query () {}
	Query (int _x, int _y, int _id) { x = _x, y = _y, id = _id; } 
	bool operator < (const Query &A) const {
		return x < A.x;
	}
} q[N];
struct scanline {
	int x, l, r, op;
	scanline () {}
	scanline (int _x, int _l, int _r, int _op) { x = _x, l = _l; r = _r; op = _op; }
	bool operator < (const scanline &A) const {
		return x < A.x;
	}
} line[K + 2][N << 1]; int lcnt[K + 2];
struct BIT {
	#define lowbit(x) x & (-x)
	int tree[N];
	void init () { memset (tree, 0, sizeof (tree)); }
	void add (int x, int val) { for ( ; x <= n; x += lowbit(x)) tree[x] += val; }
	int query (int x) { int res = 0; for ( ; x > 0; x -= lowbit (x)) res += tree[x]; return res; }
} T[K + 2];
bool vis[N];
int pos[N];
vector <int> vec[N];
int point[N];
int ans[N][K + 2];
signed main()
{
	n = read (), m = read (); int mx = 0;
	for (int i = 1; i <= n; i++) a[i] = read (), vec[a[i]].pb (i), mx = max (mx, a[i]), vis[a[i]] = true;
	for (int i = 0; i <= mx + 1; i++) vec[i].pb (n + 1);
	for (int i = 1; i <= m; i++) q[i].x = read (), q[i].y = read (), q[i].id = i;
    sort (q + 1, q + m + 1);
	for (int i = 1; i <= n; i++)
	{
		int x = a[i], y = a[i];
		int l1, r1, r2, lst = pos[a[i]];
		pos[a[i]] = i;
		for (int j = 1; j <= K; j++)
		{
			l1 = max (pos[x - 1], pos[y + 1]); r1 = min (pos[x], pos[y]);
			if (r1 <= lst && lst) break; // 去重
			if (l1 > r1)
			{
				while (pos[x - 1] > r1) x--, j++;
				while (pos[y + 1] > r1) y++, j++;
				if (j > K) break;
				l1 = max (pos[x - 1], pos[y + 1]);
			} // 扩展一位的同时扩入了多个,这也说明当前枚举的长度 k 在右端点为 i 时不存在
			r2 = min (vec[x - 1][point[x - 1]], vec[y + 1][point[y + 1]]) - 1;
            // vector O(1) 处理nxt
			line[j][++lcnt[j]] = scanline (max (l1 + 1, lst + 1), i, r2, 1);
			line[j][++lcnt[j]] = scanline (r1 + 1, i, r2, -1);
			if (!pos[x - 1] && !pos[y + 1]) break;
			else pos[x - 1] > pos[y + 1] ? x-- : y++;
		}
		point[a[i]]++;
	}
	for (int k = 1; k <= K; k++)
	{
        sort (line[k] + 1, line[k] + lcnt[k] + 1);
		int now = 1;
		for (int i = 1; i <= m; i++)
		{
			while (now <= lcnt[k] && line[k][now].x <= q[i].x) 
                T[k].add(line[k][now].l, 1 * line[k][now].op),
                T[k].add(line[k][now].r + 1, -1 * line[k][now].op),
				now++;
			(ans[q[i].id][k] += T[k].query(q[i].y)) %= 10;
		}
	}
	for (int i = 1; i <= m; i++, printf("\n"))
		for (int k = 1; k <= K; k++)
			printf ("%d", ans[i][k]);
    return 0;
}
posted @ 2022-11-12 16:25  TheDarkEmperor  阅读(21)  评论(0编辑  收藏  举报