题意很裸...但做起来大有文章...
区间成段更新并求和..第一想到的应该是线段树...但是题目给的区间求和是无法直接下手
之所以不好直接下手..是因为不能成段的更新和统计和..由于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;
}