传送门
做这题之前最好先做一下这题 洛谷 线段树2
说回来这题,比起线段树2,其实就多了一个操作,求幂方和.区间赋值是可以由前两个操作推导出来的,我们先×0在+x就可以了.考虑如何求幂方和
因为线段树维护的信息要满足区间可加性.幂方和是满足的.所以就维护吧.
我们维护三个sum,一个一次幂,一个二次幂,一个三次幂,记为s1,s2,s3.
这三个的维护s1其实就是区间和.s2和s3推导一下就可以得到维护的公式了.因为只有区间加和区间乘,只要在这两个操作的时候顺便维护一下s1s2s3就行了.s1很好维护,区间乘也很好维护,讲一下s2的区间加(同理可得s3的区间加).
al - ar + d. 平方和即是(al+d)2.展开(al2+2d×al+d2).那整体就是加了2d×s1+d2×len(len是区间的长度).s3同理.
线段树敲不熟练的用暴力分块也一样可以做.8s的时限应该勉强够用
线段树代码
#pragma GCC optimize(2)
#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=b;++i)
#define afir(i,a,b) for(int i=a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define L 2*i
#define R 2*i+1
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
const int p = 1e4+7;
inline int read(){
int x = 0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Tree{
int l,r;
LL add,mul,s[3],len;
}tree[N*4];
LL a[N];
void push_up(int i){
tree[i].s[0] = tree[L].s[0] + tree[R].s[0];
tree[i].s[1] = tree[L].s[1] + tree[R].s[1];
tree[i].s[2] = tree[L].s[2] + tree[R].s[2];
}
void push_add(int i,LL v){
LL s[3],len = tree[i].len;
s[0] = tree[i].s[0];s[1] = tree[i].s[1];s[2] = tree[i].s[2];
tree[i].s[0] = (s[0] + v*len)%p;
tree[i].s[1] = (s[1] + (2*v*s[0] + v*v*len%p))%p;
tree[i].s[2] = (s[2] + (v*v*v*len%p + 3*v*v*s[0]%p + 3*v*s[1]%p))%p;
}
void push_mul(int i,LL v){
LL s[3];
s[0] = tree[i].s[0];s[1] = tree[i].s[1];s[2] = tree[i].s[2];
tree[i].s[0] = s[0]*v%p;
tree[i].s[1] = s[1]*v*v%p;
tree[i].s[2] = s[2]*v*v*v%p;
}
void push_down(int i){
LL add = tree[i].add,mul = tree[i].mul;
if(mul != 1){
tree[L].add = tree[L].add*mul%p;
tree[R].add = tree[R].add*mul%p;
tree[L].mul = tree[L].mul*mul%p;
tree[R].mul = tree[R].mul*mul%p;
push_mul(L,mul);
push_mul(R,mul);
}
if(add){
tree[L].add = (tree[L].add + add)%p;
tree[R].add = (tree[R].add + add)%p;
push_add(L,add);
push_add(R,add);
}
tree[i].add = 0; tree[i].mul = 1;
}
void build(int i,int l,int r){
tree[i] = {l,r,0,1,0,0,0,r-l+1};
if(l >= r) return;
int mid = l + r >> 1;
build(L,l,mid);
build(R,mid+1,r);
}
void Add(int i,int l,int r,LL v){
if(tree[i].l >= l && tree[i].r <= r){
tree[i].add = (tree[i].add + v)%p;
push_add(i,v);
return;
}
push_down(i);
int mid = tree[i].l + tree[i].r >> 1;
if(r <= mid) Add(L,l,r,v);
else if(l > mid) Add(R,l,r,v);
else{
Add(L,l,mid,v);
Add(R,mid+1,r,v);
}
push_up(i);
}
void Mul(int i,int l,int r,LL v){
if(tree[i].l >= l && tree[i].r <= r){
tree[i].add = tree[i].add*v%p;
tree[i].mul = tree[i].mul*v%p;
push_mul(i,v);
return;
}
push_down(i);
int mid = tree[i].l + tree[i].r >> 1;
if(r <= mid) Mul(L,l,r,v);
else if(l > mid) Mul(R,l,r,v);
else{
Mul(L,l,mid,v);
Mul(R,mid+1,r,v);
}
push_up(i);
}
LL query(int i,int l,int r,int v){
if(tree[i].l >= l && tree[i].r <= r) return tree[i].s[v];
push_down(i);
int mid = tree[i].l + tree[i].r >> 1;
if(r <= mid) return query(L,l,r,v);
else if(l > mid) return query(R,l,r,v);
else return (query(L,l,mid,v) + query(R,mid+1,r,v))%p;
}
int main(){
int n,m;
while(1){
n = read();m = read();
if(!n && !m) break;
build(1,1,n);
while(m--){
int op,l,r,v;
op = read();l = read();r = read();v = read();
if(op == 1) Add(1,l,r,v);
else if(op == 2) Mul(1,l,r,v);
else if(op == 3){
Mul(1,l,r,0);
Add(1,l,r,v);
}
else printf("%lld\n",query(1,l,r,v-1));
}
}
return 0;
}