bzoj2120 数颜色 分块

       据说暴力可过2200ms。。。然而我写分块也才1200ms。。然而很多人写分块跑不过暴力。。。

       参考了将狼踩尽(http://www.cnblogs.com/jianglangcaijin/p/3460040.html)的思路。假设分为m块(注意不是每块m个)。用sum[x][y][z]表示在块x~y中颜色z(经过离散化以后)的个数,val[x][y]表示在x~y块中不同颜色的个数。这样应该就比较容易明白了。

       修改:看u会影响那些val[x][y],然后根据sum的值得到val是否要加减,具体看代码;

       查询:不能简单的像普通的分块那样进行,要利用现有的。比如查询x~y,它经过的完整的块是u~v,那么我们就可以假设将x~u以及v~y中的颜色插入到val[u][v]中,然后就可以根据修改的模式进行查询了,答案就是val[u][v]。但是最后要复原。

       初始化时间复杂度为O(m^2N),修改时最坏经过O(m^2)次,查询主要是零碎部分O(N/m)。总的时间复杂度O(m^2N+M(m^2+N/m)),可以看到,当m=N^(1/3)时时间复杂度最小为O(N^(5/3))。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
using namespace std;

int n,m,size,cnt,tot,blg[20005],a[20005],f[1000005],l[30],r[30],sum[30][30][20005],val[30][30];
void qry(int x,int y){
	int i,u=blg[x]+1,v=blg[y]-1;
	for (i=x; i<l[u] && i<=y; i++){
		if (!sum[u][v][a[i]]) val[u][v]++; sum[u][v][a[i]]++;
	}
	for (i=y; i>r[v] && i>=x; i--){
		if (!sum[u][v][a[i]]) val[u][v]++; sum[u][v][a[i]]++;
	}
	printf("%d\n",val[u][v]);
	for (i=x; i<l[u] && i<=y; i++){
		sum[u][v][a[i]]--; if (!sum[u][v][a[i]]) val[u][v]--;
	}
	for (i=y; i>r[v] && i>=x; i--){
		sum[u][v][a[i]]--; if (!sum[u][v][a[i]]) val[u][v]--;
	}
}
int main(){
	scanf("%d%d",&n,&m); int i,j,k;
	size=(int)pow(n,2.0/3); cnt=n/size;
	for (i=1; i<=cnt; i++){
		l[i]=r[i-1]+1; r[i]=l[i]+size-1;
	}
	if (r[cnt]<n) r[cnt]=n;
	for (i=1; i<=n; i++){
		scanf("%d",&k); if (!f[k]) f[k]=++tot; a[i]=f[k];
	}
	for (i=1; i<=cnt; i++) for (j=l[i]; j<=r[i]; j++) blg[j]=i;
	for (i=1; i<=cnt; i++) for (j=i; j<=cnt; j++)
		for (k=l[i]; k<=r[j]; k++){
			if (!sum[i][j][a[k]]) val[i][j]++; sum[i][j][a[k]]++;
		}
	while (m--){
		char ch=getchar(); while (ch<'A' || ch>'Z') ch=getchar();
		int x,y; scanf("%d%d",&x,&y);
		if (ch=='Q') qry(x,y); else{
			for (i=1; i<=cnt; i++) for (j=i; j<=cnt; j++) if (l[i]<=x && x<=r[j]){
				sum[i][j][a[x]]--; if (!sum[i][j][a[x]]) val[i][j]--;
			}
			if (!f[y]) f[y]=++tot; a[x]=f[y];
			for (i=1; i<=cnt; i++) for (j=i; j<=cnt; j++) if (l[i]<=x && x<=r[j]){
				if (!sum[i][j][a[x]]) val[i][j]++; sum[i][j][a[x]]++;
			}
		}
	}
	return 0;
}

by lych

2016.2 5


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>