题意很裸...但做起来大有文章...
区间成段更新并求和..第一想到的应该是线段树...但是题目给的区间求和是无法直接下手
之所以不好直接下手..是因为不能成段的更新和统计和..由于k<=5...可以把右边的括号展开...
只看右边括号..... k=0 : (i-l+1)^0 = 1
k=1 : (i-l+1)^1 = i + ( 1-l )
k=2 : (i-l+1)^2 = i^2 + 2*(1-l) + (1-l)^2
............
把 (i-l+1)^k 看成 (i+ (1-l))^k...就可以很方便的进行二项式展开...k=x , (i+(1-l))^x =sigma(C(K,i)*i^x*(1-l)^(x-i)) ( 0<=i<=k )
而如此可以得到要求的和为 = A*Sigma(ai) + B*Sigma(ai*i) + C*Sigmal(ai*i^2)....( l<=i<=r , A,B,C..为常数)
k至多为5...那么0,1,2,3,4,5...构造六颗线段树..分别代表ai , ai*i , ai*i*i , ai*i*i*i , ai*i*i*i*i , ai*i*i*i*i*i
做了一些预处理后....这六颗树都可以很方便的成段更新和统计...
可以事先将1+1+1+1+1+.....1+2+3+4+5.... ; 1+4+9+16+25.... ; 1+8+27+64+125....这类表打出来
比如说要更新 3 5 段 为 3
那么 sum[0](3~5) = (5 - 2)*3 = 9
sum[1](3~5) = (25 - 3)*3 = 72
sum[2](3~5) = (55 - 5)*3 = 150
,,,,,,,,,,,,,,
当要询问区间时...用这六棵线段树算出这个区间的ai , ai*i , ai*i*i , ai*i*i*i , ai*i*i*i*i , ai*i*i*i*i*i..再用二项式的方法得出答案就好了...
由于一些去摸运算..注意时时刻刻取摸..并且当计算出负数时要加回正数( (a-b)%c 是大于0的 但(a%c - b%c)可能会小于0,,改回正数..也就是结果+MOD)
Program:
#include<iostream> #include<stack> #include<queue> #include<stdio.h> #include<algorithm> #include<string.h> #include<cmath> #define ll long long #define oo 1000000007 #define MAXN 100005 using namespace std; struct node { ll a[6]; }; ll sum[6][MAXN<<2],col[MAXN<<2],data[6][MAXN],C[6][6],POW[MAXN][6]; node ADD(node a,node b) { int i; for (i=0;i<6;i++) a.a[i]=(a.a[i]+b.a[i])%oo; return a; } void PreWork() { int i,j; ll x; memset(data,0,sizeof(data)); POW[0][0]=1; for (i=1;i<=100000;i++) { x=1; for (j=0;j<6;j++) POW[i][j]=x,data[j][i]=(data[j][i-1]+x)%oo,x=(x*i)%oo; } for (i=0;i<6;i++) C[i][0]=C[i][i]=1; for (i=1;i<6;i++) for (j=1;j<6;j++) C[i][j]=C[i-1][j-1]+C[i-1][j]; return ; } void PushDown(int l,int r,int now) { int i,mid=(l+r)/2; ll x; if (col[now]<0) return; col[now<<1]=col[(now<<1)|1]=col[now]; for (i=0;i<6;i++) { x=data[i][mid]-data[i][l-1]; if (x<0) x=oo+x; sum[i][now<<1]=(col[now]*x)%oo; x=data[i][r]-data[i][mid]; if (x<0) x=oo+x; sum[i][(now<<1)|1]=(col[now]*(data[i][r]-data[i][mid]))%oo; } col[now]=-1; return; } void update(int L,int R,int c,int l,int r,int now) { int i; ll x; if (L<=l && R>=r) { col[now]=c; for (i=0;i<6;i++) { x=data[i][r]-data[i][l-1]; if (x<0) x=oo+x; sum[i][now]=(c*x)%oo; } return; } PushDown(l,r,now); int mid=(l+r)>>1; if (L<=mid) update(L,R,c,l,mid,now<<1); if (mid<R) update(L,R,c,mid+1,r,(now<<1)|1); for (i=0;i<6;i++) sum[i][now]=(sum[i][now<<1]+sum[i][(now<<1)|1])%oo; return; } node query(int L,int R,int l,int r,int now) { node h,p; if (L<=l && R>=r) { for (int i=0;i<6;i++) h.a[i]=sum[i][now]; return h; } PushDown(l,r,now); int mid=(l+r)>>1; memset(h.a,0,sizeof(h.a)); if (L<=mid) h=ADD(h,query(L,R,l,mid,now<<1)); if (mid<R) h=ADD(h,query(L,R,mid+1,r,(now<<1)|1)); return h; } ll getans(node h,ll L,int k) { int i; ll t,ans=0; for (i=0;i<=k;i++) { t=(C[k][i]*h.a[i])%oo; t=(t*POW[L-1][k-i])%oo; if ((k-i)%2) t=-t; ans=(ans+t)%oo; } return (ans+oo)%oo; } int main() { int i,n,m,x,y,z; char c; PreWork(); while (~scanf("%d%d",&n,&m)) { memset(sum,0,sizeof(sum)); memset(col,-1,sizeof(col)); for (i=1;i<=n;i++) { scanf("%d",&x); update(i,i,x,1,n,1); } while (m--) { do { c=getchar(); } while(c!='=' && c!='?'); scanf("%d%d%d",&x,&y,&z); if (c=='?') printf("%I64d\n",getans(query(x,y,1,n,1),x,z)); else update(x,y,z,1,n,1); } } return 0; }