题解:SP7299 MULTQ3 - Multiples of 3

Pro

完成两种操作:

  • 区间加 11
  • 区间有多少个倍数为 33 的倍数。

Sol

Subtask 1

考虑每次循环 [l,r][l,r],时间复杂度 O(nq)O(nq)

Subtask 2

因为 O(qn)O(q\sqrt n) 可过,所以考虑分块。

Update

定义 modk,i(k{0,1,2})mod_{k,i}(k\in \{0,1,2\}) 表示第 ii 块中对 33 取模等于 kk 的数的个数。

对于散块维护 modk,imod_{k,i}

对于整块,因为每次加 11,原来模 33 等于 kk 的数,会变成模 (k+1)mod3(k+1)\bmod 3。即:modk,i=mod(k1)mod3,i=mod(k+2)mod3,imod'_{k,i}=mod_{(k-1)\bmod 3,i}=mod_{(k+2)\bmod 3,i}

注意还有个区间加,带一个 lazy_tag\operatorname{lazy\_tag} 就行。

Query

对于散块直接计算。

对于整块,直接取 mod0,imod_{0,i}

时间复杂度

分块时间复杂度 O(qn)O(q\sqrt n)

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
int id[100005],ll[350],n,tot=0;
int a[100005],lzy[350];
int mod[3][350];
void build()
{
	int kc=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		id[i]=(i-1)/kc+1;
		if(id[i]!=id[i-1]) ll[++tot]=i;
		mod[0][tot]++;
	}
	ll[tot+1]=n+1;
}
int query_sk(int l,int r)
{
	int ans=0;
	for(int i=l;i<=r;i++) ans+=((a[i]+lzy[id[l]])%3==0);
	return ans;
}
int query_zk(int k){return mod[0][k];}
void update_sk(int l,int r,int k)
{
	for(int i=0;i<3;i++) mod[i][k]=0;
	for(int i=l;i<=r;i++) a[i]++;
	for(int i=ll[k];i<ll[k+1];i++)
		mod[(a[i]+lzy[k])%3][k]++;
}
void update_zk(int k) // 0 1 2
{
	lzy[k]++;
	swap(mod[0][k],mod[1][k]); // 1 0 2
	swap(mod[0][k],mod[2][k]); // 2 0 1
}
int query(int l,int r)
{
	int L=id[l],R=id[r];
	if(L==R) return query_sk(l,r);
	else
	{
		int ans=query_sk(l,ll[L+1]-1)+query_sk(ll[R],r);
		for(int i=L+1;i<R;i++) ans+=query_zk(i);
		return ans;
	}
}
void update(int l,int r)
{
	int L=id[l],R=id[r];
	if(L==R) update_sk(l,r,L);
	else
	{
		update_sk(l,ll[L+1]-1,L);
		update_sk(ll[R],r,R);
		for(int i=L+1;i<R;i++) update_zk(i);
	}
}
signed main()
{
	int m;
	cin>>n>>m;
	build();
	while(m--)
	{
		int op,l,r;
		cin>>op>>l>>r;
		l++,r++;
		if(op==0) update(l,r);
		else cout<<query(l,r)<<endl;
	}
	return 0;
}
posted @ 2024-11-12 11:17  sLMxf  阅读(21)  评论(0)    收藏  举报  来源