梦幻布丁

157 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情

[HNOI2009] 梦幻布丁

题目描述

nn 个布丁摆成一行,进行 mm 次操作。每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。

例如,颜色分别为 1,2,2,11,2,2,1 的四个布丁一共有 33 段颜色.

输入格式

第一行是两个整数,分别表示布丁个数 nn 和操作次数 mm
第二行有 nn 个整数,第 ii 个整数表示第 ii 个布丁的颜色 aia_i
接下来 mm 行,每行描述一次操作。每行首先有一个整数 opop 表示操作类型:

  • op=1op = 1,则后有两个整数 x,yx, y,表示将颜色 xx 的布丁全部变成颜色 yy
  • op=2op = 2,则表示一次询问。

输出格式

对于每次询问,输出一行一个整数表示答案。

样例 #1

样例输入 #1

4 3
1 2 2 1
2
1 2 1
2

样例输出 #1

3
1

提示

样例 1 解释

初始时布丁颜色依次为 1,2,2,11, 2, 2, 1,三段颜色分别为 [1,1],[2,3],[4,4][1, 1], [2, 3], [4, 4]
一次操作后,布丁的颜色变为 1,1,1,11, 1, 1, 1,只有 [1,4][1, 4] 一段颜色。

数据规模与约定

对于全部的测试点,保证 1n,m1051 \leq n, m \leq 10^51ai,x,y1061 \leq a_i ,x, y \leq 10^6

提示

请注意,不保证颜色的编号不大于 nn,也不保证 xyx \neq ymm 不是颜色的编号上限。

分析

这题是个板子题,是莫队根号分块的板子题,这题暴力显然是不行的啦,用pos来记录一下位置,然后分块求,使复杂度讲到nsqrt(n)。

代码

//Awwawa! Dis cold yis ratten buy Pikachu!
#include <bits/stdc++.h>
#define ll long long
#define mp make_pair
#define fi first
#define se second
#define pb push_back
#define vi vector<int>
#define pi pair<int, int>
#define mod 998244353
using namespace std;
const int N=100020;
const int M=1000010;
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*10+ch-'0'; 
		ch=getchar();
	}
	return x*f;
}
int n,m;
int a[N];
vi pos[M];
int ans=0;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		pos[a[i]].pb(i);
	}
	for(int i=1;i<=n+1;i++){
		ans+=(a[i]!=a[i-1]);
	}
	for(int i=1;i<=m;i++){
		int op;
		cin>>op;
		if(op==2) cout<<ans-1<<endl;
		else{
			int x,y;
			cin>>x>>y;
			if(x==y) continue;
			else{
				if(pos[x].size()>pos[y].size()){
					swap(pos[x],pos[y])
					;
				}
				auto modify=[&](int p,int col){
					ans-=(a[p]!=a[p-1])+(a[p]!=a[p+1]);
					a[p]=col;
					ans+=(a[p]!=a[p-1])+(a[p]!=a[p+1]); 
				};
				int col=a[pos[y][0]];
				for(auto p:pos[x]){
					modify(p,col);
					pos[y].pb(p);
				}
			}
			pos[x].clear();
		}
	}
	return (0-0);
}

希望能帮助到大家qaq!