• 【BZOJ2453】维护队列/【BZOJ2120】数颜色 分块


    【BZOJ2453】维护队列

    Description

    你小时候玩过弹珠吗?
    小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N。为了整个队列鲜艳美观,小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少。当然,A有时候会依据个人喜好,替换队列中某个弹珠的颜色。但是A还没有学过编程,且觉得头脑风暴太浪费脑力了,所以向你来寻求帮助。

    Input

    输入文件第一行包含两个整数N和M。
    第二行N个整数,表示初始队列中弹珠的颜色。
    接下来M行,每行的形式为“Q L R”或“R x c”,“Q L R”表示A想知道从队列第L个弹珠到第R个弹珠中,一共有多少不同颜色的弹珠,“R x c”表示A把x位置上的弹珠换成了c颜色。

    Output

    对于每个Q操作,输出一行表示询问结果。

    Sample Input

    2 3
    1 2
    Q 1 2
    R 1 2
    Q 1 2

    Sample Output

    2
    1

    HINT

    对于100%的数据,有1 ≤ N ≤ 10000, 1 ≤ M ≤ 10000,小朋友A不会修改超过1000次,所有颜色均用1到10^6的整数表示。

    题解:听说这题卡树套树(没写过)

    我们还是考虑分块

    一个经典的分块思路:我们用pre[i]表示i之前第一个与i颜色相同的弹珠,这样在询问时,在[l,r]中pre[i]小于l的i的个数就是答案。发现这个pre数组有一个很好的性质,即单调性,因此我们想到在教主的魔法那里用的办法,新建一个数组,块内按pre排序,这样就能二分查找出一个块内pre[i]<l的i的个数,然后就很容易了

    现在我们考虑怎么修改,题目里说保证修改次数不多于1000,因此网上很多大犇都用的是暴力修改,但毕竟我们是读书人,怎能用暴力呢?

    其实不用暴力修改也很(ju)容(ma)易(fan)

    我们新开一个数组s[i][j]表示第i个块里,颜色为j个弹珠的个数。然后修改时我们要找到这个点:前面+后面,与原来的颜色相同的+与修改后颜色相同的,最近的点是哪个,(说白了就是维护pre数组),那我们就先在自己的块里找,然后在一个一个块扫,直接看s数组就可以知道块里有没有要找的数,然后在进入块里一个一个扫。时间复杂度:扫块sqrt(n),扫块内sqrt(n),所以加起来还是sqrt(n),比起那些O(n)来说简直有莫大的优越感

    现在问题来了,你们想看压行的代码还是不压行的代码?反正我压了

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    const int maxn=11010;
    int n,m,siz,ans,tot;
    char str[5];
    int p[maxn],q[maxn],ref[1000010],pre[maxn],s[110][maxn];
    void rebuild(int x)
    {
    	for(int i=x*siz;i<x*siz+siz;i++)	q[i]=pre[i];
    	sort(q+x*siz,q+x*siz+siz);
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	siz=int(sqrt(1.0*n));
    	int i,j,k,a,b,l,r,mid,flag;
    	memset(ref,-1,sizeof(ref));
    	for(i=0;i<n;i++)
    	{
    		scanf("%d",&p[i]);
    		if(ref[p[i]]==-1)	ref[p[i]]=++tot;
    		p[i]=ref[p[i]],s[i/siz][p[i]]++,pre[i]=-1,flag=0;
    		if(s[i/siz][p[i]]>1)	for(k=i-1;k>=i/siz*siz&&!flag;k--)	if(p[k]==p[i])	pre[i]=k,flag=1;
    		for(j=i/siz-1;j>=0&&!flag;j--)	if(s[j][p[i]])	for(k=j*siz+siz-1;k>=j*siz&&!flag;k--)	if(p[k]==p[i])	pre[i]=k,flag=1;
    	}
    	for(i=n;i<(n-1)/siz*siz+siz;i++)	p[i]=pre[i]=1<<30;
    	for(i=0;i*siz<=n;i++)	rebuild(i);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%s%d%d",str,&a,&b);
    		if(str[0]=='Q')
    		{
    			a--,b--,ans=0;
    			if(a/siz==b/siz)
    			{
    				for(j=a;j<=b;j++)	ans+=pre[j]<a;
    				printf("%d
    ",ans);
    				continue;
    			}
    			for(j=a;j<a/siz*siz+siz;j++)	ans+=pre[j]<a;
    			for(j=b/siz*siz;j<=b;j++)	ans+=pre[j]<a;
    			for(j=a/siz*siz+siz;j<b/siz*siz;j+=siz)
    			{
    				l=j,r=j+siz;
    				while(l<r)
    				{
    					mid=l+r>>1;
    					if(q[mid]<a)	l=mid+1;
    					else	r=mid;
    				}
    				ans+=l-j;
    			}
    			printf("%d
    ",ans);
    		}
    		else
    		{
    			flag=0,a--;
    			if(ref[b]==-1)	ref[b]=++tot;
    			b=ref[b];
    			if(s[a/siz][p[a]]>1)	for(k=a+1;k<a/siz*siz+siz&&!flag;k++)	if(pre[k]==a)	pre[k]=pre[a],flag=1;
    			for(j=a/siz+1;j*siz<n&&!flag;j++)	if(s[j][p[a]])	for(k=j*siz;k<j*siz+siz&&!flag;k++)	if(pre[k]==a)	pre[k]=pre[a],flag=1,rebuild(j);
    			s[a/siz][p[a]]--,flag=0;
    			if(s[a/siz][b])	for(k=a-1;k>=a/siz*siz&&!flag;k--)	if(p[k]==b)	pre[a]=k,flag=1;
    			for(j=a/siz-1;j>=0&&!flag;j--)	if(s[j][b])	for(k=j*siz+siz-1;k>=j*siz&&!flag;k--)	if(p[k]==b)	pre[a]=k,flag=1,rebuild(j);
    			if(!flag)	pre[a]=-1;
    			flag=0;
    			if(s[a/siz][b])	for(k=a+1;k<a/siz*siz+siz&&!flag;k++)	if(p[k]==b)	pre[k]=a,flag=1;
    			for(j=a/siz+1;j*siz<n&&!flag;j++)	if(s[j][b])	for(k=j*siz;k<j*siz+siz&&!flag;k++)	if(p[k]==b)	pre[k]=a,flag=1,rebuild(j);
    			s[a/siz][b]++,p[a]=b;
    			rebuild(a/siz);
    		}
    	}
    	return 0;
    }
  • 相关阅读:
    魔兽70TBC猎人常用宏
    魔兽70TBC猎人宝宝技能汇总
    redis常用概念
    mongodb分片集群开启安全认证
    mongodb集群搭建(分片+副本)
    mongodb 用户权限控制
    greenplum数据迁移
    greenplum资源队列
    GreenPlum 集群常用命令
    COCOS 实现Player玩家控制的左右控制,实现马里奥一样的移动
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/6591154.html
Copyright © 2020-2023  润新知