首页 > 其他分享 >【题解】P5397 [Ynoi2018] 天降之物

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

时间:2023-01-15 15:11:15浏览次数:45  
标签:Ynoi2018 int 题解 之物 pos ++ putc ans 根号

码力人的甜品,口嗨者的末路。

感觉手牵手那个题才是第四分块正体,这个不如叫最初根号分治。

思路

根号分治。

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

先考虑不带修的问题。

对于大于根号次的值显然至多只有根号个,可以暴力 \(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;
}

标签:Ynoi2018,int,题解,之物,pos,++,putc,ans,根号
From: https://www.cnblogs.com/lingspace/p/p5397.html

相关文章

  • 【题解】P5692 [MtOI2019]手牵手走向明天
    春节前做大分块是什么奇妙传统吗……这个题好想是好想,但是写起来就是另外一回事了……思路分块。第四分块加强版。区间查询,根号分治做法寄了。看到合并颜色可以想到......
  • Codeforces 732 Div2 A-D题解
    感觉做过这场啊,要不就是看过A题AquaMoonandTwoArrays问前加后减能不能把A变成B,首先这个貌似是经典老题了,无论怎么操作数列总和不变,如果和不相同,变不了,其他情况暴力判......
  • ARC153 ABC 题解
    A【题意】给定\(N\),求第\(N\)个满足以下条件的数:它是一个\(9\)位数,没有前导\(0\)。它的第一位等于它的第二位。它的第五位等于它的第六位。它的第七位等于它......
  • XMU 2023.1.14 题解汇总
    A、CF1779A原题B、https://www.cnblogs.com/wondering-world/p/17038860.htmlC、https://www.luogu.com.cn/problem/solution/P4305D、快速幂模板点击查看代码#incl......
  • DTOJ-2023-01-02-测试-题解
    (2023省选模拟Round#4)之前感冒了一阵子,错过了两场省选模拟,不过我不打算补(乐成绩:0+42+0(就是说T1写挂了)A题目链接题目大意小\(\omega\)最近学习了分治\(\text{......
  • 【题解】P4565 [CTSC2018]暴力写挂
    能写点分为什么要写这种玄学东西。思路边分树合并。首先考虑点分,发现只会T飞的做法。但是答案的形式有点意思,换一下写法:\(ans=\frac{1}{2}\max(\operatorname{dis......
  • Codeforces 1630 E Making It Bipartite 题解 (Dilworth定理)
    题目链接首先可以想到把题目中的那张图G建出来,由于要求这张图是二分图,把它复制一遍(\(G\toG'\)),然后对于每个u,连一条无向边\(u-u'\),这样就变成了最大独立集问题。但是一......
  • Codeforces 1630 E Making It Bipartite 题解 (Dilworth定理)
    题目链接首先可以想到把题目中的那张图G建出来,由于要求这张图是二分图,把它复制一遍(\(G\toG'\)),然后对于每个u,连一条无向边\(u-u'\),这样就变成了最大独立集问题。但是一......
  • P1390 公约数的和 题解
    传送门题意:求出\(\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\gcd(i,j)\)原式\(=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i-1}\gcd(i,j)\)\(=\sum\limits_{d=1......
  • P4220 题解
    前言题目传送门!更好的阅读体验?思路代码为了使代码更容易通过,可以像我一样膜拜大佬,获得随机种子,通过的概率更大。#include<iostream>#include<cstdio>#include<......