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;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n,m,block,blo[N],s[N],col[N*100],now[N];
int Ans[N],l,r,cntq,cntc,t,ans;

struct Query{
    int l,r,tim,id;
}q[N];  
struct Change{
    int pos,New,Old;
}c[N];
bool cmp(Query A, Query B){
    if (blo[A.l] != blo[B.l]) return A.l < B.l;
    if (blo[A.r] != blo[B.r]) return A.r < B.r;
    return A.tim < B.tim;
}
void act(int x, int y){
    col[x] += y; 
    if (y > 0) ans += (col[x] == 1);  //ans 记录当前区间的答案.
    if (y < 0) ans -= (col[x] == 0);
}
void going(int x, int d){
    if (x <= r && x >= l) act(d,1),act(s[x],-1); //如果在在正确的区间,需要修改答案.
    s[x] = d;  //直接复制为 d.
}
int main(){
    char ss[10];
    int x,y;
    scanf("%d%d",&n,&m);  block = pow(n,0.66666);
    for (int i = 1; i <= n; ++i){
        scanf("%d",&s[i]); now[i] = s[i]; blo[i] = (i - 1) / block + 1;
    }
    for (int i = 1; i <= m; ++i){
        scanf("%s%d%d",ss,&x,&y);
        if (ss[0] == 'Q') {
            q[++cntq] = (Query){x,y,cntc,cntq}; //把询问的放在一起,结构体里面放 id,还有修改了多少次
        } else {
            c[++cntc] = (Change){x,y,now[x]}; now[x] = y; //把修改的放在一起,记录修改 了多少次.
        }
    }
    sort(q+1,q+cntq+1,cmp);   //把询问排序,关键字 l, r ,tim.
    l = 1; r = 0; t = 0;
    for (int i = 1; i<= cntq; ++i){
        while(t < q[i].tim) going(c[t+1].pos, c[t+1].New),t++; //首先时间回溯.找到正确的修改时间.
        while(t > q[i].tim) going(c[t].pos, c[t].Old), t--;

        while(l < q[i].l) act(s[l], -1),l++;
        while(l > q[i].l) act(s[l-1],1), l--;
        while(r < q[i].r) act(s[r+1],1),r++;
        while(r > q[i].r) act(s[r],-1), r--;
        Ans[q[i].id] = ans;
    }
    for (int i = 1; i <= cntq; ++i)
        printf("%d\n",Ans[i]);
    return 0;


}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值