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

题目链接


题目大意:

带修莫队 ---- P1903 [国家集训队]数颜色 / 维护队列 带修莫队模板_第1张图片


解题思路:

带修改的莫队

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

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

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

思路

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

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

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

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

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

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

反之如果改多了就改回来

说的听绕口的

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

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


AC code :

#include 
#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;
}

你可能感兴趣的:(#,莫队,数据结构,c语言,算法)