UVA12345 带修改的莫队

题目大意:给你[0,n-1]的序列,有m个操作,Q l r询问[L,R-1]区间内的不同的数字的个数,M x y为修改a[x]=y。

思路:利用可修改的莫队,将(L,R,x)作为三元组进行分块。L的块作为第一关键字,R的块作为第二关键字,第x次修改作为第三关键字。每一次扩展区间后模拟修改过程。

俗话说得好 莫队就是分块之后暴力乱搞233

#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#include<cstdio>
#include<stdio.h>
#include<set>
#include<map>
#include<deque>
#include<stack>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<list>
#include<fstream>
#include<bitset>
#include <ctime>
using namespace std;
//加速cin
/*ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);*/
typedef long long ll;
const int maxn = 50010;
char * cp = (char *)malloc(20000000);
inline void in(int &x) {
	while (*cp<'0' || *cp>'9')++cp;
	for (x = 0;*cp >= '0'&&*cp <= '9';)x = x * 10 + (*cp++^'0');
}
inline void in(char &x) {
	while (*cp == '\n' || *cp == ' ')++cp;
	x = (*cp);
}
int Lid[maxn], Rid[maxn], a[maxn];
int pre[maxn], ct[maxn * 21];//ct存取a[x]在区间内出现的次数
int cg[maxn][3];//记录修改
int now;
int ans[maxn];
struct ask
{
	int l, r, c, id;
	friend bool operator < (const ask& x, const ask& y)
	{
		if (Lid[x.l] == Lid[y.l])
		{
			if (Rid[x.r] == Rid[y.r])
				return x.c < y.c;
			else return Rid[x.r] < Rid[y.r];
		}
		return Lid[x.l] < Lid[y.l];
	}
}q[maxn];
void up(int l, int r, int& im, int t)
{
	while (im != t)
	{
		im++;
		a[cg[im][2]] = cg[im][1];
		if (cg[im][2] < l || cg[im][2] > r) { continue; }
		ct[cg[im][1]]++;ct[cg[im][0]]--;
		if (ct[cg[im][1]] == 1 && cg[im][1]!=cg[im][0])now++;
		if (ct[cg[im][0]] == 0 && cg[im][1] != cg[im][0])now--;
	}
}
void down(int l, int r, int& im, int t)
{
	while (im != t)
	{
		a[cg[im][2]] = cg[im][0];
		if (cg[im][2] < l || cg[im][2] > r) { im -= 1; continue; }
		ct[cg[im][0]]++;ct[cg[im][1]]--;
		if (ct[cg[im][0]] == 1 && cg[im][1] != cg[im][0])now++;
		if (ct[cg[im][1]] == 0 && cg[im][1] != cg[im][0])now--;
		im--;
	}
}
void add(int x, int d)
{
	ct[a[x]] += d;
	if (ct[a[x]] == 0 && d < 0)now--;
	else if (ct[a[x]] == 1 && d > 0)now++;
}
int main()
{
	int n, m, sz;
	fread(cp, 1, 20000000, stdin);
	in(n), in(m);
	sz = sqrt(n);
	for (int i = 0;i < n;i++)
	{
		in(a[i]);
		Rid[i] = Lid[i] = i / sz + 1;//初始化分块
		pre[i] = a[i];
	}
	char cmd;int l, r, cnt = 1,id = 0;
	for (int i = 0;i < m;i++)
	{
		in(cmd);in(l), in(r);
		if (cmd == 'Q')
		{
			q[id].id = id;
			q[id].l = l;
			q[id].r = r - 1;
			q[id++].c = cnt - 1;
		}
		else
		{
			cg[cnt][2] = l;    //第三位存修改的位置
			cg[cnt][0] = pre[l];    //第一位存原来的数
			cg[cnt++][1] = r;        //第二位存新的数
			pre[l] = r;
		}
	}
	sort(q, q + id);
	l = 0, r = 0, cnt = 0, ct[a[0]] = 1, now = 1;
	for (int i = 0;i < id;i++)
	{
		while (l < q[i].l) { add(l, -1);l++; }
		while (r > q[i].r) { add(r, -1);r--; }
		while (l > q[i].l) { add(l - 1, 1);l--; }
		while (r < q[i].r) { add(r + 1, 1);r++; }
		if (cnt > q[i].c)//模拟从第cnt次更新到第q[i].c的更新
			down(l, r, cnt, q[i].c);
		else if (cnt < q[i].c)
			up(l, r, cnt, q[i].c);
		ans[q[i].id] = now;
	}
	for (int i = 0;i < id;i++)printf("%d\n", ans[i]);
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值