1.nfls #1194 3的倍数区间
题意
给定一个长度为的数组,有次修改,每次把数组中某个元素改成其他的数或者询问一个区间中有多少个子区间满足和为 \(3\) 的倍数。
\(n,m\le 3\times 10^5\)。
思路
和为倍数的,得先想到把余数分类开来,反正这里余数只有 \(0,1,2\)。
注意到题目问的是子区间的个数。联想到维护最大子段和的思路:在线段树上维护区间的和、前缀和、后缀和、子段和答案。
于是同样考虑,维护区间 \(\bmod 3\) 的和 \(sum\)、前后缀余 \(0,1,2\) 的桶 l[],r[],以及区间答案 \(ans\),容易用两个桶维护 \(ans\),将余数和为 \(0\) 的合并,考虑合并函数:
node add(const node a,const node b)
{node ret;ret.sum=(a.sum+b.sum)%3;ret.ans=a.ans+b.ans+a.r[0]*b.l[0]+a.r[1]*b.l[2]+a.r[2]*b.l[1];//乘法原理计算方案数for(int i=0;i<3;i++){ret.l[i]+=a.l[i];ret.l[(i+a.sum)%3]+=b.l[i];//囊括一整段ls查看触及到rs的l端 ret.r[i]+=b.r[i];ret.r[(i+b.sum)%3]+=a.r[i];} return ret;
}
对于单点修改直接对四者初始化,照线段树常例 pushup 和 query。平日里求和的 query 是可以直接相加,但是这里使用 add 函数,不能用一个全空的结构体变量参与运算,因此要把合并区间答案的给拎出来——对于返回结构体的 query,使用 add 合并区间答案的,都适用以下写法:
node query(ll u,ll l,ll r,ll ql,ll qr)
{if(ql<=l&&r<=qr)return T[u];ll mid=(l+r)>>1;if(qr<=mid)return query(ls,l,mid,ql,qr);if(ql>mid)return query(rs,mid+1,r,ql,qr);return add(query(ls,l,mid,ql,qr),query(rs,mid+1,r,ql,qr));}
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ls u<<1
#define rs u<<1|1
const ll N=3e5+9;
ll n,Q;
ll a[N];
struct SegT
{struct node{ll sum;//模3区间和 ll ans;ll l[3]={0,0,0},r[3]={0,0,0};//前后缀和每个余数的桶 void init(ll k){sum=k%3;ans=(k%3==0);for(int i=0;i<3;i++)l[i]=r[i]=0;l[k%3]=r[k%3]=1;}}T[N<<2];node add(const node a,const node b){node ret;ret.sum=(a.sum+b.sum)%3;ret.ans=a.ans+b.ans+a.r[0]*b.l[0]+a.r[1]*b.l[2]+a.r[2]*b.l[1];for(int i=0;i<3;i++){ret.l[i]+=a.l[i];ret.l[(i+a.sum)%3]+=b.l[i];//囊括一整段ls查看触及到rs的l端 ret.r[i]+=b.r[i];ret.r[(i+b.sum)%3]+=a.r[i];} return ret;}void pushup(ll u){T[u]=add(T[ls],T[rs]);}void build(ll u,ll l,ll r){if(l==r){T[u].init(a[l]);return;}ll mid=(l+r)>>1;build(ls,l,mid);build(rs,mid+1,r);pushup(u);}void modify(ll u,ll l,ll r,ll x,ll k){if(l==r){T[u].init(k);return;}ll mid=(l+r)>>1;if(x<=mid)modify(ls,l,mid,x,k);if(x>mid)modify(rs,mid+1,r,x,k);pushup(u);}node query(ll u,ll l,ll r,ll ql,ll qr){if(ql<=l&&r<=qr)return T[u];ll mid=(l+r)>>1;if(qr<=mid)return query(ls,l,mid,ql,qr);if(ql>mid)return query(rs,mid+1,r,ql,qr);return add(query(ls,l,mid,ql,qr),query(rs,mid+1,r,ql,qr));}
}A;
int main()
{scanf("%lld%lld",&n,&Q);for(int i=1;i<=n;i++)scanf("%lld",&a[i]);A.build(1,1,n);while(Q--){ll op,l,r;scanf("%lld%lld%lld",&op,&l,&r);if(op==1)A.modify(1,1,n,l,r);else printf("%lld\n",A.query(1,1,n,l,r).ans);}return 0;
}
