带修莫队 ---- P1903 [国家集训队]数颜色 / 维护队列 带修莫队模板

题目链接


题目大意:

在这里插入图片描述


解题思路:

带修改的莫队

首先我们要知道,普通的莫队算法是不资瓷修改操作的,

不过后人对莫队算法加以改进

发明了资瓷修改的莫队算法

思路

在进行修改操作的时候,修改操作是会对答案产生影响的(废话)

那么我们如何避免修改操作带来的影响呢?

首先我们需要把查询操作和修改操作分别记录下来。

在记录查询操作的时候,需要增加一个变量来记录离本次查询最近的修改的位置

然后套上莫队的板子,与普通莫队不一样的是,你需要用一个变量记录当前已经进行了几次修改

对于查询操作,如果当前改的比本次查询需要改的少,就改过去

反之如果改多了就改回来

说的听绕口的

比如,我们现在已经进行了3次修改,本次查询是在第5次修改之后,那我们就执行第4,5次修改

这样就可以避免修改操作对答案产生的影响了


AC code :

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = N;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}

int n, m;
int where[maxn]; // 统计左右端点在哪个块?
int Cnum, Qnum;
// Cnum 修改操作的个数,Qnum 查询操作的个数
int color[maxn], a[maxn], ans, base, out[maxn];
// base是块的大小,out是用来输出答案的,ans是统计答案用的, color 统计颜色个数
struct Quary {
	int l, r, pre, id;
}Q[maxn];
// 魔改版的莫队排序函数很快
int comp(const Quary &a,const Quary &b) {
    return a.l / base == b.l / base ? a.r / base == b.r / base ? a.pre < b.pre : a.r < b.r : a.l < b.l;
}

struct Change {
	int pos, val;
}C[maxn];

inline void Add(int val) {
	if(++ color[val] == 1) ans ++;
}

inline void Delete(int val) {
	if(-- color[val] == 0) ans --;
}

inline void work(int now, int i) {
	if(C[now].pos >= Q[i].l && C[now].pos <= Q[i].r) { //注意这里要判断是否在查询范围内?
		if(-- color[a[C[now].pos]] == 0) ans --;
		if(++ color[C[now].val] == 1) ans ++; 
	}
	swap(C[now].val,a[C[now].pos]);
	// 你把pos改成了val。下次你再修改就是相当于还原,所以就是把val和a值直接交换就可以了
}

inline void Moqueue() {
	int l = 1, r = 0, now = 0;
	for(int i = 1; i <= Qnum; ++ i) {
		while(l > Q[i].l) Add(a[--l]);
		while(r < Q[i].r) Add(a[++r]);
		while(l < Q[i].l) Delete(a[l++]);
		while(r > Q[i].r) Delete(a[r--]);
		while(now < Q[i].pre) work(++now,i);
		while(now > Q[i].pre) work(now--,i);
		out[Q[i].id] = ans;
	}
	for(int i = 1; i <= Qnum; ++ i)
	  printf("%d\n",out[i]);
}

int main() {
	read(n,m);
	base = pow(n,0.666); // n^(2/3)次方 块的大小
	for(int i = 1; i <= n; ++ i) {
		read(a[i]);
		where[i] = (i - 1) / base + 1;
	}	
	while(m --) {
		char op[5];
		scanf("%s",op);
		if(*op == 'Q') {
			++ Qnum;
			read(Q[Qnum].l,Q[Qnum].r);
			Q[Qnum].id = Qnum;
			Q[Qnum].pre = Cnum;
		} else {
			++ Cnum;
			read(C[Cnum].pos,C[Cnum].val);
		}
	}
	sort(Q+1,Q+1+Qnum,comp);
	Moqueue();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
后缀自动机被广泛应用于OI竞赛中,特别是在字符串相关的问题中。它是一种高效的数据结构,能够有效地解决各种字符串匹配、模式匹配和计等问题。 首先,后缀自动机可以用于解决最长公共子串和最长公共子序列等问题。对于给定的两个字符串,可以将其加入到后缀自动机中,并通过动态规划的方式求解最长公共子串或子序列的长度。 其次,后缀自动机还可以用于解决多次询问下的子串出现次问题。通过构建全局后缀自动机,可以在O(n)的时间复杂度内预处理字符串,并在O(m)的时间复杂度内得出任意子串的出现次,其中n为字符串长度,m为询问总。 另外,后缀自动机还可以用于解决包含多模式匹配的问题。通过将模式串加入到后缀自动机中,并预处理自动机的fail指针,可以在O(n)的时间复杂度内找到所有模式串在文本中的出现位置。这在处理大规模的文本匹配问题时非常有用。 此外,后缀自动机还可以进行字符串的字典序统计。通过在构建自动机时记录每个节点的信息,可以在O(n)的时间复杂度内得到字符串的字典序第k小/大的子串。 总之,后缀自动机在OI竞赛中有着广泛的应用,能够解决各种字符串相关的问题。通过巧妙地构建自动机,并充分利用其性质,可以实现高效的字符串算法,为解决复杂的字符串问题提供了有力的工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值