题目大概是这样的:
- 给出一个长度为nnn的序列,mmm次询问,qqq为参数,有两种操作
- 给区间∀i∈[l,r]{\forall}i\in[l,r]∀i∈[l,r]的数字加上qi−lq^{i-l}qi−l
- 询问区间[l,r][l,r][l,r]的数字和
- n,m<=105,1<q<=109n,m<=10^5,1<q<=10^9n,m<=105,1<q<=109
分析:
既然题目说q>1q>1q>1,那么就可以用等比数列求和公式来计算加了多少了,而且对于所有的操作,q都是一致的,那么懒标记可以记录这段区间的要加的等比数列的首项就可以!
下传标记的时候注意左子树可以直接加过来,因为他们的首项相同,右子树就需要补上qmid+1−lq^{mid+1-l}qmid+1−l了。
听老师YY的一道题,也没有找到交的地方,自己写了写暴力对拍了好多数据都过了,需要注意乘法的时候需要用快速乘,因为逆元还有标记还有次幂数组乘起来会炸long long。
输入格式就是:n,m,qn,m,qn,m,q
序列元素
mmm次操作,id,l,rid,l,rid,l,r,id为1是修改,2是查询。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
const int mod=1e9+7;
ll ci[maxn];
ll sum[maxn<<2|1];
ll lazy[maxn<<2|1];
ll q;
ll ni;
ll quick(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void pushup(int k){
sum[k]=sum[k<<1]+sum[k<<1|1];
sum[k]%=mod;
}
ll ksc(ll x,ll y,ll mod) {
x%=mod;
y%=mod;
return (x*y-(ll)((long double)x/mod*y)*mod+mod)%mod;
}
void build(int l,int r,int k){
lazy[k]=0;
if(l==r){
scanf("%lld",&sum[k]);
return ;
}
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
pushup(k);
}
void pushdown(int l,int r,int k){
if(lazy[k]){
int mid=(l+r)>>1;
lazy[k<<1]+=lazy[k];
lazy[k<<1]%=mod;
lazy[k<<1|1]+=lazy[k]*ci[mid-l+1]%mod;
lazy[k<<1|1]%=mod;
sum[k<<1]+=ksc(lazy[k]*ci[mid-l+1]-lazy[k]+mod,ni,mod);
sum[k<<1|1]+=ksc(lazy[k]*ci[r-l+1]-lazy[k]*ci[mid-l+1]+mod,ni,mod);
sum[k<<1]%=mod;
sum[k<<1|1]%=mod;
lazy[k]=0;
}
}
void updata(int l,int r,int k,int L,int R){
if(l>=L&&r<=R){
sum[k]+=ksc(ci[r-L+1]-ci[l-L]+mod,ni,mod);
sum[k]%=mod;
lazy[k]+=ci[l-L];
lazy[k]%=mod;
return ;
}
pushdown(l,r,k);
int mid=(l+r)>>1;
if(L<=mid) updata(l,mid,k<<1,L,R);
if(R>mid) updata(mid+1,r,k<<1|1,L,R);
pushup(k);
}
ll myfind(int l,int r,int k,int L,int R){
if(l>=L&&r<=R) return sum[k];
pushdown(l,r,k);
int mid=(l+r)>>1;
ll res=0;
if(L<=mid) res+=myfind(l,mid,k<<1,L,R);
if(R>mid) res=(res+myfind(mid+1,r,k<<1|1,L,R))%mod;
pushup(k);
return res%mod;
}
int main(){
freopen("in.txt","r",stdin);
freopen("out1.txt","w",stdout);
int n,m,l,r,id;
scanf("%d%d%lld",&n,&m,&q);
ni=quick(q-1,mod-2);
ci[0]=1;
for(int i=1;i<maxn;++i)
ci[i]=ci[i-1]*q%mod;
build(1,n,1);
while(m--){
scanf("%d%d%d",&id,&l,&r);
if(id==1) updata(1,n,1,l,r);
else printf("%lld\n",myfind(1,n,1,l,r));
}
return 0;
}
暴力:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
ll a[maxn];
const int mod=1e9+7;
ll ci[maxn];
int main(){
freopen("in.txt","r",stdin);
freopen("out2.txt","w",stdout);
int n,m,id,l,r;
ll q;
scanf("%d%d%lld",&n,&m,&q);
ci[0]=1;
for(int i=1;i<maxn;++i) ci[i]=ci[i-1]*q%mod;
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
while(m--){
scanf("%d%d%d",&id,&l,&r);
if(id==1) for(int i=l;i<=r;++i) a[i]=(a[i]+ci[i-l])%mod;
else{
ll res=0;
for(int i=l;i<=r;++i) res=(res+a[i])%mod;
printf("%lld\n",res);
}
}
return 0;
}
造数据
/*
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5;
const int qq=1e9;
const int mod=1e9+7;
int main(){
int n,m,q,id,l,r;
freopen("in.txt","w",stdout);
srand(unsigned(time(0)));
n=rand()%maxn+1;
m=rand()%maxn+1;
q=rand()%qq;
if(q<=1) q=2;
printf("%d %d %d\n",n,m,q);
for(int i=1;i<=n;++i)
printf("%d ",rand()%qq);
printf("\n");
while(m--){
id=rand()%2+1;
l=rand()%n+1;
r=rand()%n+1;
if(l>r) swap(l,r);
printf("%d %d %d\n",id,l,r);
}
return 0;
}
*/