Loading

【题解】P5397 [Ynoi2018] 天降之物

思路

根号分治。

对于每个值,把它们分成出现大于根号次和小于等于根号次两类。

先考虑不带修的问题。

对于大于根号次的值显然至多只有根号个,可以暴力 \(O(n)\) 预处理出和它有关的答案。

对于小于等于根号次的值,显然可以暴力归并或者 vector 二分求答案,归并单次的复杂度是根号。

带修的话,分讨一下贡献。

  1. 两个根号次以内的值,合并后还是根号次以内

直接归并。

  1. 两个大于根号次的值

因为至多只会有根号次这样的合并,所以可以暴力。

  1. 一个根号次以内的值和一个大于根号次的值

这个比较麻烦,要拆一下贡献。

可以把这个小于根号次的值暂时先归并到另一个值内,然后询问时和预处理出的答案一起算贡献。

归并累积到大于根号次时只能暴力重构,但是因为至多有根号次,所以复杂度也是对的。

注意可能会有合并未出现的值一类的边界情况,所以还要套路地维护每个值真实情况下代表的值。

时间复杂度 \(O(n \sqrt{n})\)

代码

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

const int maxn = 1e5 + 5;
const int sq = 320;
const int inf = 0x3f3f3f;

int n, m, cur, lim;
int a[maxn], sz[maxn], id[maxn], fa[maxn];
int ans[sq][maxn];
vector<int> pos[maxn];

namespace IO
{
    //by cyffff
	int len = 0;
	char ibuf[(1 << 20) + 1], *iS, *iT, out[(1 << 26) + 1];
	#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
	#define reg register

	inline int read()
    {
		reg char ch = gh();
		reg int x = 0;
		reg char t = 0;
		while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
		while (ch >= '0' && ch <= '9') x = x * 10 + (ch ^ 48), ch = gh();
		return t ? -x : x;
	}

	inline void putc(char ch) { out[len++] = ch; }

	template<class T>

	inline void write(T x)
    {
		if (x < 0) putc('-'), x = -x;
		if (x > 9) write(x / 10);
		out[len++] = x % 10 + 48;
	}

	inline void flush()
    {
		fwrite(out, 1, len, stdout);
		len = 0;
	}
}
using IO::read;
using IO::write;
using IO::flush;
using IO::putc;

inline void build(int x, int _id = 0)
{
    id[x] = _id ? _id : ++cur;
    int t = id[x];
    memset(ans[t], 0x3f, (n + 1) * sizeof(int));
    for (int i = 1, j = inf; i <= n; i++)
        if (a[i] == x) j = 0;
        else ans[t][a[i]] = min(ans[t][a[i]], ++j);
    for (int i = n, j = inf; i >= 1; i--)
        if (a[i] == x) j = 0;
        else ans[t][a[i]] = min(ans[t][a[i]], ++j);
    vector<int> emp;
    ans[t][x] = 0, pos[x].swap(emp);
}

inline void merge(int x, int y)
{
    int lx = pos[x].size(), ly = pos[y].size(), i, j;
    vector<int> res;
    for (i = 0, j = 0; (i < lx) && (j < ly); ) res.push_back(pos[x][i] < pos[y][j] ? pos[x][i++] : pos[y][j++]);
    while (i < lx) res.push_back(pos[x][i++]);
    while (j < ly) res.push_back(pos[y][j++]);
    pos[y] = res;
}

inline int qry(int x, int y)
{
    int lx = pos[x].size(), ly = pos[y].size(), ans = inf, i, j;
    if ((lx == 0) || (ly == 0)) return inf;
    for (i = 0, j = 0; (i < lx) && (j < ly); ) ans = min(ans, (pos[x][i] < pos[y][j] ? pos[y][j] - pos[x][i++] : pos[x][i] - pos[y][j++]));
    while (i < lx) ans = min(ans, pos[x][i++] - pos[y][ly - 1]);
    while (j < ly) ans = min(ans, pos[y][j++] - pos[x][lx - 1]);
    return ans;
}

inline void modify(int x, int y)
{
    int fx = fa[x], fy = fa[y];
    if ((!sz[fx]) || (fx == fy)) return;
    if (sz[fx] > sz[fy]) fa[y] = fx, fa[x] = n + 1, swap(fx, fy);
    else fa[x] = n + 1;
    if ((fx > n) || (fy > n)) return;
    x = fx, y = fy;
    if (sz[y] <= lim)
    {
        if (sz[x] + sz[y] <= lim)
        {
            for (int p : pos[x]) a[p] = y;
            for (int i = 1; i <= cur; i++) ans[i][y] = min(ans[i][y], ans[i][x]);
            merge(x, y);
        }
        else
        {
            for (int i = 1; i <= n; i++)
                if (a[i] == x) a[i] = y;
            build(y);
        }
    }
    else if (sz[x] <= lim)
    {
        if (sz[x] + pos[y].size() <= lim)
        {
            for (int p : pos[x]) a[p] = y;
            for (int i = 1; i <= cur; i++) ans[i][y] = min(ans[i][y], ans[i][x]);
            merge(x, y);
        }
        else
        {
            for (int i = 1; i <= n; i++)
                if (a[i] == x) a[i] = y;
            build(y, id[y]);
        }
    }
    else
    {
        for (int i = 1; i <= n; i++)
            if (a[i] == x) a[i] = y;
        build(y, id[y]);
    }
    sz[y] += sz[x], sz[x] = 0;
    vector<int> emp;
    pos[x].swap(emp);
}

inline int query(int x, int y)
{
    x = fa[x], y = fa[y];
    if ((!sz[x]) || (!sz[y])) return -1;
    if (x == y) return 0;
    if (sz[x] > sz[y]) swap(x, y);
    if (sz[y] <= lim) return qry(x, y);
    else if (sz[x] <= lim) return min(ans[id[y]][x], qry(x, y));
    return min(qry(x, y), min(ans[id[x]][y], ans[id[y]][x]));
}

int main()
{
    n = read(), m = read(), lim = sqrt(n);
    for (int i = 1; i <= n; i++) sz[a[i] = read()]++, pos[a[i]].push_back(i), fa[i] = i;
    for (int i = 0; i <= n; i++)
        if (sz[i] > lim) build(i);
    int last_ans = 0;
    while (m--)
    {
        int opt, x, y;
        opt = read(), x = read() ^ last_ans, y = read() ^ last_ans;
        if (opt == 1) modify(x, y);
        else
        {
            last_ans = query(x, y);
            if (~last_ans) write(last_ans), putc('\n');
            else last_ans = 0, putc('I'), putc('k'), putc('a'), putc('r'), putc('o'), putc('s'), putc('\n');
        }
    }
    flush();
    return 0;
}
posted @ 2023-01-15 15:04  kymru  阅读(25)  评论(0编辑  收藏  举报