首页 > 其他分享 >[POI2008] POC-Trains 题解

[POI2008] POC-Trains 题解

时间:2024-07-30 16:17:43浏览次数:8  
标签:const POI2008 int 题解 tree tot fa Trains 集合

前言

题目链接:洛谷

时间复杂度和输入同阶的做法。

题意简述

有 \(n\)(\(n \leq 10^3\))个长 \(m\) 的字符串,\(q\)(\(q \leq 10^5\))次操作,交换两个字符串的两个字符。问每个字符串在所有时刻,最多有几个和它相等。

题目分析

套路做法

看到字符串相等,想到使用哈希。但是要支持修改,怎么办呢?上数据结构!当然不是,把哈希的结果看做一个数字,每次把某一位的值扣掉再加上就行了。即减去该位权值乘上原值,加上权值乘上修改后的值。

至于询问,每次操作后不可能 \(\Theta(n^2)\) 地弄。发现只会和当前修改的两个串有关,故 \(\Theta(n)\) 把这两个串的结果统计一遍。其他的串,维护一个 \(cur_i\) 表示当前 \(i\) 能和几个字符串相等。如果 \(i\) 和修改之前的串相等,就减去,如果和修改后的串相等了,再加上,最后和答案取个 \(\max\) 即可。

时间复杂度 \(\Theta(nm + nq)\)。可过此题。当然,可以优化。

优化做法

尝试从哈希角度分析。发现,一个字符串不断从一个相同哈希值得集合中,移动到另一个集合中,而答案就是这段时间内,该集合大小的最大值。如果考虑在删除的时候统计答案,就是在该元素加入集合后,该集合大小的最大值。

尝试把集合大小独立出来,看做单独的一个权值。这样就可以把删除这个操作去掉。即我们可以把元素扩展,额外附加它在加入集合时集合的大小,并不做删除,而是直接将集合大小减一。这样查询,就成了一个后缀最大值。

对于一条链,我们会扫过很多次,不妨从这里优化,即去除重复操作。想到,如果扫过了一个值,那下次不用再从原来的位置开始再扫一遍,而是,把当前扫过来的最值记下来,下次直接从这里开始扫就行了。很类似并查集的路径压缩。

时间复杂度呢?每条边只会被经过一遍,和 \(n + q\) 是同阶的。总的时间复杂度是 \(\Theta(nm + q)\) 的,非常快。

代码

naive code

代码

优化

哈希值域太小会 WA,所以使用了 unordered_map,对时间复杂度没有影响。略去了快读,是最优解

#include <cstdio>
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;

const int N = 1010;
const int M = 110;
const int Q = 100010;
const int mod = 1e9 + 11;
const int bas = 1331;

inline int add(int a, int b) {
	return a + b >= mod ? a + b - mod : a + b;
}

inline int sub(int a, int b) {
	return a - b < 0 ? a - b + mod : a - b;
}

inline int mul(int a, int b) {
	return 1ll * a * b % mod;
}

int n, m, q;
char str[N][M];
int hsh[N], res[N], pw[M];
int whr[N];

struct node {
	int fa, ans;
} tree[N + Q * 2];
int tot;

unordered_map<int, int> ysiz, ylst;

int query(int x) {
	if (tree[x].fa == x) return tree[x].ans;
	int res = query(tree[x].fa);
	tree[x].fa = tree[tree[x].fa].fa;
	return tree[x].ans = max(tree[x].ans, res);
}

void update(int x) {
	int bl = hsh[x];
	whr[x] = ++tot, ++ysiz[bl];
	tree[tot] = {tot, ysiz[bl]};
	tree[ylst[bl]].fa = tot;
	ylst[bl] = tot;
}

signed main() {
	fread(buf, 1, MAX, stdin);
	read(n), read(m), read(q);
	pw[0] = 1;
	for (int i = 1; i <= m; ++i)
		pw[i] = mul(pw[i - 1], bas);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			hsh[i] = add(mul(hsh[i], bas), str[i][j] = readchar());
		}
		update(i);
	}
	for (int i = 1, a, x, b, y; i <= q; ++i) {
		read(a), read(x), read(b), read(y);
		res[a] = max(res[a], query(whr[a])), --ysiz[hsh[a]];
		if (a != b) res[b] = max(res[b], query(whr[b])), --ysiz[hsh[b]];
		hsh[a] = sub(hsh[a], mul(pw[m - x], str[a][x]));
		hsh[b] = sub(hsh[b], mul(pw[m - y], str[b][y]));
		swap(str[a][x], str[b][y]);
		hsh[a] = add(hsh[a], mul(pw[m - x], str[a][x]));
		hsh[b] = add(hsh[b], mul(pw[m - y], str[b][y]));
		update(a);
		if (a != b) update(b);
	}
	for (int i = 1; i <= n; ++i) {
		printf("%d\n", max(res[i], query(whr[i])));
	}
	return 0;
}

标签:const,POI2008,int,题解,tree,tot,fa,Trains,集合
From: https://www.cnblogs.com/XuYueming/p/18332502

相关文章

  • 题解:[ABC363D] Palindromic Number
    提示特定位数的回文数数量是可以快速计算出来的,对于特定的位数\(n\),第一位由于不能有前导\(0\),共有\(9\)种选择,而从第\(2\)位到第\(\frac{n+1}{2}\)位都有\(10\)种选择(一个回文数完全由它的前半部分确定)。所以\(n\)位数中共有\(S(n)=9*10^\frac{n-1}{2}......
  • P3811 【模板】模意义下的乘法逆元 题解
    【模板】模意义下的乘法逆元题目背景这是一道模板题题目描述给定n,pn,pn,p求......
  • 题解:Codeforces Round 962 (Div. 3) D
    D.Funtimelimitpertest:2secondsmemorylimitpertest:256megabytesinput:standardinputoutput:standardoutputCountingisFun!—satyam343Giventwointegers\(n\)and\(x\),findthenumberoftriplets(\(a,b,c\))ofpositiveintegerss......
  • [USACO1.5] 八皇后 Checker Challenge 题解
    [USACO1.5]八皇后CheckerChallenge题目描述一个如下的\(6\times6\)的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。上面的布局可以用序列\(2\4\6\1\3\5\)来描述,第\(i\)个数字表示在......
  • 题解:P10815 【模板】快速读入
    闲着没事儿水篇tj题目大意题目大意极其粗暴,记得\(10^8\times10^8=10^{16}>2^{31}-1\)会爆int,开longlong就好。于是这个题就变成了一个读入输出优化模板题。这不又回去了。另外,输入输出常数优化也很常用,抢最优解和骗分时都可以用上。1cin/cout版本操作ios::......
  • 奇怪的汉诺塔 - 题解
    奇怪的汉诺塔时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++256MB,其他语言512MB描述汉诺塔问题,条件如下:这里有\(A\)、\(B\)、\(C\)和\(D\)四座塔。这里有\(n\)个圆盘,\(n\)的数量是恒定的。每个圆盘的尺寸都不相同。所有的圆盘在开始时都堆叠在塔\(A\)......
  • 昆虫繁殖 - 题解
    昆虫繁殖时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++256MB,其他语言512MB描述科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强。每对成虫过\(x\)个月产\(y\)对卵,每对卵要过两个月长成成虫。假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后......
  • 【入门】汉诺塔的移动次数 - 题解
    【入门】汉诺塔的移动次数时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++128MB,其他语言256MB描述汉诺塔的问题大家都已经很熟悉了,有三个柱子,每个柱子上有一些大小不一的金片,要把金片从\(A\)柱移动到\(C\)柱,可以借助\(B\)柱,请问\(n\)个金片的情况下,需要最少移......
  • 【基础】递归问题—汉诺塔 - 题解
    【基础】递归问题—汉诺塔时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++127MB,其他语言254MB描述汉诺塔(又称河内塔)问题是印度的一个古老的传说。开天辟地的神勃拉玛在一个庙里留下了三根金刚石的棒,第一根上面套着\(64\)个圆的金片,最大的一个在底下,其余一个比一个小......
  • 放苹果 - 题解
    时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++256MB,其他语言512MB描述把\(M\)个同样的苹果放在\(N\)个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)\(5,1,1\)和\(1,5,1\)是同一种分法。输入描述第一行是测试数据的数目\(t\)(\(0≤t≤20\))。......