[国家集训队]数颜色 (带修莫队/树套树)

题目

戳这洛谷

思路

首先,我想到了莫队。
排序+分块后,查询的时间被打乱了。
对于值的修改,该怎么办呢?这时候我们就使用带修莫队。带修莫队和普通莫队思路基本上是一样的,只是加入了一条时间轴。记录当前状态的时间,当转移到下一个状态时,如果当前时间小于下一时间,那么就减去现在的,换为并加上之前记录的;反正进行回退,并记录这一时间的状态为Pre,方便之后的回退。

详见代码

Code:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 133335
#define MAXM 1000005
#define LL long long
#define Int register int
using namespace std;
inline void read(int &x)
{
	x = 0;
	int f = 1;
	char s = getchar();
	while (s < '0' || s > '9')
	{
		if (s == '-')
			f = -1;
		s = getchar();
	}
	while (s >= '0' && s <= '9')
	{
		x = (x << 3) + (x << 1) + (s ^ 48);
		s = getchar();
	}
	x *= f;
}
struct node
{
	int l, r, Time, Index;
}As[MAXN];
int Kuai;
bool cmp(node x,node y)
{
	if (x.l / Kuai != y.l / Kuai)
		return x.l < y.l;
	if (x.r / Kuai != y.r / Kuai)
		return x.r < y.r;
	return x.Time < y.Time;
}
int a[MAXN], totQ, totR, Pre[MAXN];
struct edon
{
	int Ct, pos, val;
}Ch[MAXN];
int Curl = 1, Curr, Ans[MAXN], cnt[MAXM], Kind, R_Index;
void Huan_a(int Ti)
{
	if (Ch[Ti].pos >= Curl && Ch[Ti].pos <= Curr)
	{
		cnt[a[Ch[Ti].pos]] --;
		if (! cnt[a[Ch[Ti].pos]])
			Kind --;
	}
	Pre[Ti] = a[Ch[Ti].pos];
	a[Ch[Ti].pos] = Ch[Ti].val;
	if (Ch[Ti].pos >= Curl && Ch[Ti].pos <= Curr)
	{
		if (! cnt[a[Ch[Ti].pos]])
			Kind ++;
		cnt[a[Ch[Ti].pos]] ++;
	}
}
void Huan_n(int Ti)
{
	if (Ch[Ti].pos >= Curl && Ch[Ti].pos <= Curr)
	{
		cnt[a[Ch[Ti].pos]] --;
		if (! cnt[a[Ch[Ti].pos]])
			Kind --;
	}
	a[Ch[Ti].pos] = Pre[Ti];
	if (Ch[Ti].pos >= Curl && Ch[Ti].pos <= Curr)
	{
		if (! cnt[a[Ch[Ti].pos]])
			Kind ++;
		cnt[a[Ch[Ti].pos]] ++;
	}
}
void Change(int Ti)
{
	while (R_Index < totR && Ch[R_Index + 1].Ct <= Ti)
		Huan_a(++ R_Index);
	while (R_Index && Ch[R_Index].Ct > Ti)
		Huan_n(R_Index --);
}
void Jia(int x)
{
	int X = a[x];
	if (! cnt[X])
		Kind ++;
	cnt[X] ++;
}
void Jian(int x)
{
	int X = a[x];
	cnt[X] --;
	if (! cnt[X])
		Kind --;
}
int main()
{
	//freopen("fuckk.in","r",stdin);
	//freopen("sad.out","w",stdout);
	int n, m;
	read( n ); read( m );
	for (Int i = 1; i <= n; ++ i)
		read( a[i] );
	for (Int i = 1; i <= m; ++ i)
	{
		char Or[2];
		scanf("%s", Or);
		if (Or[0] == 'Q')
		{
			totQ ++;
			As[totQ].Index = totQ;
			As[totQ].Time = i;
			read( As[totQ].l );
			read( As[totQ].r );
		}
		else
		{
			totR ++;
			Ch[totR].Ct = i;
			read( Ch[totR].pos );
			read( Ch[totR].val );
		}
	}
	Kuai = pow(n * n, 0.33333); 
	sort(As + 1, As + 1 + totQ, cmp);
	for (Int i = 1; i <= totQ; ++ i)
	{
		Change( As[i].Time );
		int L = As[i].l, R = As[i].r;
		while (Curl < L)
			Jian(Curl ++);
		while (Curl > L)
			Jia(-- Curl);
		while (Curr < R)
			Jia(++ Curr);
		while (Curr > R)
			Jian(Curr --);
		int ind = As[i].Index;
		Ans[ind] = Kind;
	}
	for (Int i = 1; i <= totQ; ++ i)
		printf("%d\n", Ans[i]);
	return 0;
}

然而,随着这份优秀的代码在bzoj上可以过,在修改数据后的洛谷上是过不了的。这个时候,我们就需要使用树套树了。详见另一份树套树题解。

—————— 坑 ——————— 还没写。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值