题目链接:Click here~~
题意:
有 n 个点,每个点有一种颜色(可能相同),两种操作:1、将区间 [a,b] 染成颜色 c ; 2、询问区间 [a,b] 中颜色为 c 的点有多少个。
解题思路:
如果颜色的种类很少(5 个以内),那么就是经典的线段树区间染色的入门题。
但此题颜色太多,虽然可以进行哈希,但是规模还是能达到 10^5,根本不能朴素的用线段树实现。
不过大家很多都用线段树加优化过的。(维护区间颜色种类的最小值和最大值,可在查询时进行优化)
但感觉靠谱的思路还是块状链表,题解叫做分段哈希。
思路是:每个块维护一个 map<color,cnt> 。
然后对于更新操作中跨过的整块,直接将对应的 map 清空,加入新的颜色,长度为 block_len,不用更新对应单点的颜色。
对于两端不够一整块的部分,由于很可能出现多种颜色,所以一定要暴力将单点颜色更新掉。
其实整体思想就是当整块的颜色不唯一时,单点的值才要对应住真实值。这样可以保证更新某个整块时是 O(1) 的。看不懂的话看代码吧。。。。。
开始怕超时不敢用 map 实现,但后来听大家说大胆用 map,于是就写了一个,终于2个小时调出来了,注意修改最后一个块时下标不要超过 n。
#include <map>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <algorithm>
using namespace std;
#define size(v) (int)v.size()
const int N = 1e5 + 5;
int n,color,a[N],block_len;
map<int,int> blocks_colors[333];
void bf_update(int l,int r)
{
int block_id = l / block_len;
if(size(blocks_colors[block_id]) == 1)
{
map<int,int>::iterator it = blocks_colors[block_id].begin();
int old_color = it->first;
int old_cnt = it->second;
if(color == old_color)
return ;
if(r - l + 1 == old_cnt)
{
blocks_colors[block_id][color] = r - l + 1;
blocks_colors[block_id].erase(it);
return ;
}
for(int i=block_len*block_id;i<n && i<block_len*(block_id+1);i++)
{
if(i >= l && i <= r)
a[i] = color;
else
a[i] = old_color;
}
blocks_colors[block_id][color] = r - l + 1;
blocks_colors[block_id][old_color] = old_cnt - (r - l + 1);
}
else
{
for(int i=l;i<=r;i++)
{
if(a[i] == color)
continue;
map<int,int>::iterator it = blocks_colors[block_id].find(a[i]);
if(it->second == 1)
blocks_colors[block_id].erase(it);
else
it->second--;
blocks_colors[block_id][color]++;
a[i] = color;
}
}
}
int bf_query(int l,int r)
{
int block_id = l / block_len;
if(blocks_colors[block_id].count(color))
{
if(size(blocks_colors[block_id]) == 1)
return r - l + 1;
else
{
int ret = 0;
for(int i=l;i<=r;i++)
if(a[i] == color)
++ret;
return ret;
}
}
else
return 0;
}
void update(int l,int r)
{
int block_id1 = l / block_len;
int block_id2 = r / block_len;
if(block_id1 == block_id2)
{
bf_update(l,r);
return ;
}
int rr = block_len * (block_id1 + 1) - 1;
int ll = block_len * block_id2;
bf_update(l,rr);
bf_update(ll,r);
for(int id=block_id1+1;id<block_id2;id++)
{
blocks_colors[id].clear();
blocks_colors[id][color] = block_len;
}
}
int query(int l,int r)
{
int block_id1 = l / block_len;
int block_id2 = r / block_len;
if(block_id1 == block_id2)
return bf_query(l,r);
int rr = block_len * (block_id1 + 1) - 1;
int ll = block_len * block_id2;
int ret = bf_query(l,rr) + bf_query(ll,r);
for(int id=block_id1+1;id<block_id2;id++)
if(blocks_colors[id].count(color))
ret += blocks_colors[id][color];
return ret;
}
void debug(int n,int max_id)
{
printf("color of a[]: ");
for(int i=0;i<n;i++)
printf("%d ",a[i]);
puts("***");
for(int i=0;i<=max_id;i++)
{
printf("the %dth block[%d,%d]=>\n",i,i*block_len,(i+1)*block_len-1);
for(map<int,int>::iterator it=blocks_colors[i].begin();it!=blocks_colors[i].end();it++)
printf("\t\t\t %d -> %d\n",it->first,it->second);
puts("");
}
}
int main()
{
//freopen("in.ads","r",stdin);
int m;
while(~scanf("%d%d",&n,&m))
{
block_len = (int)sqrt(n * 1.0);
//block_len = 400;
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
int block_id_max = n / block_len;
for(int i=0;i<=block_id_max;i++)
blocks_colors[i].clear();
for(int i=0;i<n;i++)
{
int block_id = i / block_len;
blocks_colors[block_id][ a[i] ]++;
}
int op,l,r;
while(m--)
{
scanf("%d%d%d%d",&op,&l,&r,&color);
if(op == 1)
update(l,r);
else
printf("%d\n",query(l,r));
}
}
return 0;
}