CodeVS5037线段树练习4加强版

链接

  http://codevs.cn/problem/5037/

题解

  基本思想:分块记录每个块内各种数字的数量,然后统计,时空复杂度O(N*sqrt(N))。

  像这种题目线段树一般做不了,因为有K种不同的数字(你不可能对每个节点都开一个大小为K的数组,但你完全可以对每一个块开一个),类似的还有K种颜色..etc。

代码

//分块
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 200100
#define maxsz 450
int lp[maxn+maxsz], cnt[maxn/maxsz+10][maxn], N, M, K, num[maxn], tag[maxn/maxsz+10],
	size;
inline int read(int x=0)
{
	char c=getchar();
	while(c<48 or c>58)c=getchar();
	while(c>=48 and c<=58)x=x*10+c-48,c=getchar();
	return x;
}
inline char getc()
{
	char c=getchar();
	while(c<'a' or c>'z')c=getchar();
	return c;
}
inline void init()
{
	int i;
	size=sqrt(N);
	for(i=0;i<N;i++)
	{
		scanf("%d",num+i);
		cnt[lp[i]=i/size][num[i]%=K]++;
	}
	for(i=N;i<=N+size;i++)lp[i]=i/size;
}
inline void add1(int x, int d)
{
	cnt[lp[x]][num[x]]--;
	cnt[lp[x]][num[x]=(num[x]+d)%K]++;
}
inline void add(int l, int r, int v)
{
	int i;
	for(i=l;lp[i]==lp[l] and i<=r;i++)add1(i,v);
	if(lp[l]^lp[r])for(i=r;lp[i]==lp[r];i--)add1(i,v);
	for(i=lp[l]+1;i<lp[r];i++)tag[i]+=v;
}
inline int count(int l, int r)
{
	int i, ans=0, t;
	for(i=l,t=tag[lp[l]]%K;lp[i]==lp[l] and i<=r;i++)
		if((!num[i] and !t) or num[i]+t==K)ans++;
	if(lp[l]^lp[r])for(i=r,t=tag[lp[r]]%K;lp[i]==lp[r];i--)
		if((!num[i] and !t) or num[i]+t==K)ans++;
	for(i=lp[l]+1;i<lp[r];i++)ans+=cnt[i][(K-(tag[i]%=K))^K?K-tag[i]:0];
	return ans;
}
int main()
{
	int l, r, v;
	char type;
	N=read(), M=read(), K=read();
	init();
	while(M--)
	{
		type=getc(), l=read()-1, r=read()-1;
		if(type=='c')printf("%d\n",count(l,r));
		else v=read()%K, add(l,r,v);
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值