带修莫队 BZOJ2120

BZOJ2120

 

思路:

同时支持修改,查询两种操作的莫队算法,就是带修莫队。

用两个结构体分别记录查询,修改操作;

 

对查询操作分区:

先将所有的查询操作分为n/unit个区块,

除了考虑l,r所在的区块,还要考虑查询的时间,均为递增排序,对所有的查询排序。

(复杂度(复杂度分析,除了要分析的那个变量是不确定的外,其他的变量全部固定不变,然后分析变量是如何变化的):

l的复杂度O(unti*m)

{只考虑l的情况,它们的l都是不同的,l至多有m种情况,每次至多变化unit*2次,所以最坏的复杂度是O(unit*m)}

r的复杂度O(n*n/unit)

{只考虑r的情况,此时l已经在同一个区间内了,r有m种情况,每次至多变化n/unit次,所以最坏的情况就是O(n*n/unit),}

tim的复杂度O((n/unit)*(n/unit))

{只考虑tim的情况,此时n与m在同一个数量级上,可以相互替换l至多前进或者后退n/unit次,r同理,

所以复杂度为O((n/unit)*(n/unit))。}

总复杂度为O(unit*n+n*n/unit+(n/unit)*(n/unit)),当unit = pow(n,2/3)时,复杂度为O(pow(n,5/3),此时的复杂度最小)

 

从区间[1,0],T = 0开始,此时ans = 0,先更新时间,然后更新左右区间的值,

然后得到这个区间内的不同颜色的笔画的数量,记录答案。

 

参考文章

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e6+10;
const double IO = 2.0/3.0;
//a数组记录每个位置糖果的不同颜色,col记录每个位置的不同颜色的数量;
//Ki数组记录每个颜色所在的区间,res数组记录最终的结果
//now数组记录每次修改后的每个位置的颜色
//ans记录某一次的区间[l,r]内的不同颜色的数量,l,r分别表示区间的两个边界,因为修改颜色时要用到,所以设置为全局变量。 
int a[maxn],col[maxn]={0},Ki[maxn]={0},res[maxn] = {0},now[maxn] = {0},ans = 0,l = 1,r = 0;
struct Node{ //记录查询操作 
	int id,l,r,tim; //id表示第几个查询操作,l,r分别表示查询区间的左右边界,tim表示这次查询之前最近的一次修改的时间。 
	Node(){}
	Node(int x,int y,int z,int w):id(x),l(y),r(z),tim(w){}
}qu[maxn];
struct node{
	int pos,nxt,pre; //pos表示修改的颜色的位置,nxt表示改变后的颜色,pre表示改变前的颜色 
	node(){}
	node(int x,int y,int z):pos(x),nxt(y),pre(z){}
}ca[maxn];
bool cmp(Node a,Node b){ //对查询数据进行分块排序,如果左右边界所在的区块相同,按照时间的先后顺序排列。 
	if(Ki[a.l]==Ki[b.l]){
		if(Ki[a.r]==Ki[b.r]) return a.tim<b.tim;
		return a.r<b.r;
	}
	return a.l<b.l;
}
void Add(int x,int d){ //修改区间[l,r]内的颜色x的数量 
	col[x]+=d; //颜色x的数量的该变量为d 
	if(d>0){ //颜色的数量增加,如果这个颜色从无到有,说明区间内的颜色种类数+1,ans+1 
		ans += (col[x]==1);
	}
	if(d<0){//颜色的数量减少,如果这个颜色的数量变为0,说明这个区间的颜色种类数-1,ans-1 
		ans -= (col[x]==0);
	}
}
void Go(int x,int y){ //将x位置的颜色修改为颜色y 
	if(l<=x&&x<=r){
		Add(y,1); //颜色y的数量+1 
		Add(a[x],-1);//颜色a[x]的数量-1 
	}
	a[x] = y;//修改a位置的颜色 
}
int main(void)
{
	int n,m;
	scanf("%d%d",&n,&m);
	int unit = (int)pow(n,IO); //对n分区 
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),now[i] = a[i],Ki[i] = i/unit+1;
	int t1 = 0,t2 = 0;
	for(int i=1;i<=m;i++){
		char ss[5];int x,y;
		scanf("%s%d%d",ss,&x,&y);
		if(ss[0]=='Q'){//查询 
			qu[++t1] = Node(t1,x,y,t2);
		}else{//修改 
			ca[++t2] = node(x,y,now[x]);now[x] = y;
		}
	}
	sort(qu+1,qu+1+t1,cmp);
	int T = 0;
	for(int i=1;i<=t1;i++){
		while(T<qu[i].tim){ //提前到tim时间 
			Go(ca[T+1].pos,ca[T+1].nxt);T++;
		}
		while(T>qu[i].tim){ //退后到tim时间 
			Go(ca[T].pos,ca[T].pre);T--;
		}
		
		while(l<qu[i].l){
			Add(a[l],-1);l++;//l右移,颜色a[l]的数量-1 
		}
		while(l>qu[i].l){
			Add(a[l-1],1);l--; //l左移,颜色a[l-1]的数量+1 
		}
		while(r<qu[i].r){
			Add(a[r+1],1);r++;//r右移,颜色a[r+1]的数量+1 
		}
		while(r>qu[i].r){
			Add(a[r],-1);r--;//r左移,颜色a[r]的数量-1 
		}
		res[qu[i].id] = ans;//记录结果。 
	}
	for(int i=1;i<=t1;i++) printf("%d\n",res[i]);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值