2632. [HZOI 2016] 数列操作d
★★★ 输入文件:segment.in
输出文件:segment.out
简单对比
时间限制:3 s 内存限制:512 MB
【题目描述】
一个长度为n的序列,一开始序列数的权值都是0,有m次操作
支持两种操作:
1 L R x,给区间[L,R]内位置为pos的数加上(pos-L)*x
0 L R,查询区间[L,R]内的权值和
最终答案对109+7取模。
【输入格式】
第一行两个数n,m,表示序列长度和操作次数
接下来m行,每行描述一个操作,有如下两种情况:
1 L R x,给区间[L,R]内位置为pos的数加上(pos−L)×x
0 L R,查询区间[L,R]内的权值和
【输出格式】
每一个0操作输出一个整数模109+7
【样例输入】
5 5 0 2 3 1 4 5 1 1 1 5 5 0 1 4 0 2 3
【样例输出】
0 30 15
【提示】
对于30%的数据 n,m<=2000
对于100%的数据,n,m<=300000
保证读入的都是非负整数,所有的x<=10000
【来源】
感谢神犇
非常感谢神犇
非常非常感谢神犇
自动选择评测机 gcc/g++4.8.5gcc/g++4.8.5 -O2gcc/g++4.8.5(C++11)gcc/g++4.6.3gcc/g++4.6.3 -O2
提交代码 Pascal C C++
#include<bits/stdc++.h> #define ll long long #define INF 1000000007 #define maxn 300005 using namespace std; ll S[maxn]; ll lz1[maxn<<2],lz2[maxn<<2];//lz1 就是记录那个-L *x的 lz2就是记录 pos*x的 ll dat[maxn<<2]; void Add(int rt,int l,int r,int ss,int tt,ll qx,ll posx){ if(ss>r||tt<l) return;//判断是否越界 if(ss<=l&&r<=tt){//全部包含 lz1[rt]=(lz1[rt]+qx)%INF;//永久化标记 lz2[rt]=(lz2[rt]+posx)%INF; dat[rt]=(dat[rt]+qx*(r-l+1)%INF+posx*((S[r]-S[l-1])%INF)%INF)%INF;// (S[r]-S[l-1])就是那一段等差数列的和 预处理出来了直接作一个差就行了 return; } int mid=(l+r)>>1; Add(rt*2,l,mid,ss,tt,qx,posx);Add(rt*2+1,mid+1,r,ss,tt,qx,posx); dat[rt]=(dat[rt*2]+dat[rt*2+1]+lz1[rt]*(r-l+1)%INF+lz2[rt]*((S[r]-S[l-1])%INF)%INF)%INF; } ll Get(int rt,int l,int r,int ss,int tt){ if(ss>r||tt<l) return 0; if(ss<=l&&r<=tt) return dat[rt]; int lll=max(l,ss); int rr=min(r,tt); int mid=(l+r)>>1; return (lz1[rt]*(rr-lll+1)%INF+lz2[rt]*((S[rr]-S[lll-1])%INF)%INF+Get(rt*2,l,mid,ss,tt)+Get(rt*2+1,mid+1,r,ss,tt))%INF; } int main(){ freopen("segment.in","r",stdin);freopen("segment.out","w",stdout); int n,m;scanf("%d%d",&n,&m);//输入 不说了 哈哈 for(int i=1;i<=n;i++) S[i]=S[i-1]+i;//这里就是预处理一个前缀和 for(int i=1;i<=m;i++){ int Type;scanf("%d",&Type); if(Type){ int l,r;ll x,posx;scanf("%d%d%lld",&l,&r,&x); posx=(INF-l)*x%INF;//posx 就是那个-L 这里加上一个INF 是因为——L是负数 会炸的!! Add(1,1,n,l,r,posx,x); } else{ int l,r;scanf("%d%d",&l,&r); printf("%lld\n",Get(1,1,n,l,r)%INF); } } return 0; }