P5397 [Ynoi2018] 天降之物

300300 篇题解当然要写个有意思的题。

lxl 给出的标答是根号分治,不过也有序列分块的做法,只不过非常需要卡常。

我们先对序列分块。接着我们考虑怎么处理询问。

显然询问的结果有两种:

  1. 在块内的答案。这种可以通过预处理 disi,j,kdis_{i,j,k} 表示第 ii 块中 jjkk 的最小距离。即我们每一块暴力 O((n)2)O((\sqrt n)^2) 枚举,显然预处理时间复杂度为 O(nn)O(n \sqrt n)。因为每一块最多只有 O(n)O(\sqrt n) 个不同数,我们可以对每块的数离散化,这样空间复杂度也可以降低到 O(nn)O(n \sqrt n)

  2. 横跨多块的答案。我们从前往后每块搜索,如果这一块有 xx,那么肯定要在前面接一个 yy,所以我们对于每一块处理 fi,xf_{i,x}ei,xe_{i,x} 表示 ii 块中 xx 第一次出现位置和最后一次出现。那么前面我们想求的就可以轻松算出了。

两种答案取 min\min 即可。

而较难的则是修改:

我们假设有一块 ii,要把所有 xx 改为 yy

  1. 如果块中没有 xx,那么跳过。

  2. 如果块中没有 yy,那么我们把 yyii 块中离散化的值改为 xx

  3. 如果即有 xx 也有 yy。我们先更新 yyffee,然后暴力枚举 kk,用 disi,x,kdis_{i,x,k} 更新 disi,y,kdis_{i,y,k}。由于离散化过,这一步是 O(n)O( \sqrt n) 的。

你可能会问这最后一步的复杂度好像是错误的。但实际上,对于某一块,如果一直是 33 操作,那么最多经过 O(n)O(\sqrt n) 次,这个块内数就会全部一样,此时只有可能进入 1122 了。那么这一块对复杂度的贡献是 O(n)O(n),所有块也就只有 O(nn)O(n \sqrt n) 了。

至此我们得到了一个 O(nn)O(n \sqrt n) 时间,O(nn)O(n \sqrt n) 空间的做法。

然而交上去,T 了一片。于是需要卡常。

几个卡常技巧:

  1. 保证数组内存访问连续。我的代码中有 vi,jv_{i,j} 表示 ii 块中 jj 离散化结果。而将其改为 vj,iv_{j,i} 后快了许多,ffee 同理。

  2. 减少不必要的循环。

  3. 调块长。经尝试,我的代码的最优块长约在 200200 左右。

  4. Fread 快读。

  5. 选个人少的时间交。

然后给一个交了几十次过了一次,再交又会 T 的代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;

const int N = 1e5 + 5, blo = 203, M = 494;

#define get(x) ((x) / blo + 1)

#define BF_SIZE 100000
bool IOerr = 0;
inline char nc() {
	static char buf[BF_SIZE], * p1 = buf + BF_SIZE, * pend = buf + BF_SIZE;
	if (p1 == pend) {
		p1 = buf;
		pend = buf + fread(buf, 1, BF_SIZE, stdin);
		if (pend == p1) { IOerr = 1; return -1; }
	}
	return *p1++;
}
inline bool bla(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline int read()
{
	char ch;
	int x = 0;
	while (bla(ch = nc()));
	if (IOerr) { return 0; }
	for (x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = (x << 1) + (x << 3) + ch - '0');
	return x;
}

int n, m, a[N];
int b[N], idx;
short v[N][M];
int f[N][M], e[N][M];
short dis[M][blo + 1][blo + 1];
int L, R;
#define gl(i) (((i) - 1) * blo)
const short INF = 32765;

inline int query(int& x, int& y)
{
	int lx = 0, ly = 0, ans = 10000000;
	for (int i = L; i <= R; i = -~i)
	{
		if (ans == 1) return ans;
		if (v[x][i] && v[y][i])
		{
			ans = min(ans, (int)dis[i][v[x][i]][v[y][i]]);
		}
		if (v[x][i] && ly)
		{
			ans = min(ans, f[v[x][i]][i] - ly);
		}
		if (v[y][i] && lx)
		{
			ans = min(ans, f[v[y][i]][i] - lx);
		}
		if (v[x][i]) lx = e[v[x][i]][i];
		if (v[y][i]) ly = e[v[y][i]][i];
	}
	return ans;
}

inline void modify(int& x, int& y)
{
	if (x == y) return;
	for (int i = 1; i <= R; i = -~i)
	{
		int xx(v[x][i]), yy(v[y][i]);
		if (!xx) continue;
		if (!yy)
		{
			v[y][i] = v[x][i];
			v[x][i] = 0;
		}
		else
		{
			f[yy][i] = min(f[yy][i], f[xx][i]);
			e[yy][i] = max(e[yy][i], e[xx][i]);
			f[xx][i] = e[xx][i] = 0;
			for (int j = 1; j <= blo; j = -~j)
			{
				dis[i][yy][j] = dis[i][j][yy] = min(dis[i][j][yy], dis[i][j][xx]);
				dis[i][xx][j] = dis[i][j][xx] = INF;
			}
			dis[i][yy][xx] = dis[i][xx][yy] = INF;
			v[x][i] = 0;
		}
	}
}

inline void write(int x)
{
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

int main()
{
	memset(dis, 0x3f, sizeof dis);
	n = read(), m = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	L = 1, R = get(n);
	int nl(1);
	int nr(blo - 1);
	idx = 0;
	for (int j(nl); j <= nr; j = -~j) b[++idx] = a[j];
	sort(b + 1, b + idx + 1);
	for (int j(nl); j <= nr; j = -~j)
	{
		int na = lower_bound(b + 1, b + idx + 1, a[j]) - b;
		v[a[j]][1] = na;
		if (!f[na][1]) f[na][1] = j;
		e[na][1] = j;
		dis[1][na][na] = 0;
	}
	for (int j(nl); j <= nr; j = -~j)
	{
		for (int k(j + 1); k <= nr; k++)
		{
			int nx(v[a[j]][1]), ny(v[a[k]][1]), g(min(dis[1][nx][ny], (short)(k - j)));
			dis[1][nx][ny] = g;
			dis[1][ny][nx] = g;
		}
	}
	for (int i = 2; i <= R; i++)
	{
		int nl(gl(i));
		int nr(gl(i + 1) - 1);
		if (nr > n) nr = n;
		idx = 0;
		for (int j = nl; j <= nr; j++) b[++idx] = a[j];
		sort(b + 1, b + idx + 1);
		for (int j(nl); j <= nr; j++)
		{
			int na(lower_bound(b + 1, b + idx + 1, a[j]) - b);
			v[a[j]][i] = na;
			if (!f[na][i]) f[na][i] = j;
			e[na][i] = j;
			dis[i][na][na] = 0;
		}
		for (int j(nl); j <= nr; j = -~j)
		{
			for (int k(j + 1); k <= nr; k = -~k)
			{
				int nx(v[a[j]][i]), ny(v[a[k]][i]), g(min(dis[i][nx][ny], (short)(k - j)));
				dis[i][nx][ny] = g;
				dis[i][ny][nx] = g;
			}
		}
	}
	int lans = 0;
	while (m--)
	{
		int opt(read()), x(read()), y(read());
		x ^= lans, y ^= lans;
		if (opt == 2)
		{
			int res(query(x, y));
			if (res > n)
			{
				printf("Ikaros\n");
				lans = 0;
			}
			else
			{
				write(lans = res);
				putchar('\n');
			}
		}
		else
		{
			modify(x, y);
		}
	}
	return 0;
}
posted @   HappyBobb  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示