bzoj 2120: 数颜色 (带修改莫队)

求区间颜色,还加上少量的修改。

具体看代码注释。

image

#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define go(i,a,b)  for (int i = a; i <= b; i++)
#define og(i,a,b)  for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 1e6+10;
int ans[N],m,n,Ans,now[N],t,l = 1,r,tim,s[N],Be[N],unit,T;
//ans 答案数组,Ans 每个区间答案, now 记录修改之后当前点的颜色。s[] 初始时的颜色。 Be 分块。
// t 表示查询区间, tim 表示时间戳,修改了多少次,
int col[N];
struct Insert{
	int l,r,ID,Tim; //左右区间,位置,这个询问在多少个修改之后。
}f[N];
struct Change{
	int pos,x,y; //位置,新颜色,旧颜色。
}g[N];
bool cmp(Insert a, Insert b){
	return Be[a.l] == Be[b.l] ? (Be[a.r] == Be[b.r] ? a.Tim < b.Tim : a.r < b.r) : a.l < b.l; 
} //多关键字排序,先 l 后 r 最后 tim
void action(int x,int d){col[x] += d; if (d > 0) Ans += col[x] == 1; if (d < 0) Ans -= col[x] == 0;} //修改区间答案。
void Updata(int x, int d){if(l <= x && x <= r) action(d,1),action(s[x],-1); s[x] = d;} //改到正常的时间戳,修改节点颜色。
int main(){
	scanf("%d%d",&n,&m);
	unit = pow(n,0.666666);
	go(i,1,n) scanf("%d",&s[i]),Be[i] = i / unit + 1, now[i] = s[i];
	go(i,1,m){
		char ch; int x,y;
		scanf(" %c%d%d",&ch,&x,&y);
		if (ch == 'Q') f[++t] = (Insert){x,y,t,tim}; else
			g[++tim] = (Change){x,y,now[x]},now[x] = y;
	}
	sort(f+1, f + t+ 1,cmp); go(i,1,t){
		while(T < f[i].Tim) Updata(g[T+1].pos,g[T+1].x),T++; //找到属于该区间的时间戳。改区间颜色。
		while(T > f[i].Tim) Updata(g[T].pos,g[T].y),T--;

		while(l < f[i].l) action(s[l],-1),l++; //正常莫队,
		while(l > f[i].l) action(s[l-1],1),l--;
		while(r < f[i].r) action(s[r+1],1),r++;
		while(r > f[i].r) action(s[r],-1),r--;
		ans[f[i].ID] = Ans;
	}
	go(i,1,t) printf("%d\n",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/81632365