#线段树,组合计数,二项式定理#CF266E More Queries to Array

洛谷传送门

CF266E传送门


分析

首先区间修改区间查询首选线段树

要找突破口,\((i-l+1)^k\)\(i\)不是定值,

显然得拆开,而且\(k\)很小,根据二项式定理,

\[\sum_{i=l}^ra_i(i-l+1)^k=\sum_{i=l}^ra_i\sum_{j=0}^kC(k,j)\times i^j\times (l-1)^{k-j}\times (-1)^{k-j} \]

\[=\sum_{j=0}^kC(k,j)\times (\sum_{i=l}^ra_i\times i^j)\times (l-1)^{k-j}\times (-1)^{k-j} \]

那么线段树叶子节点维护\(a_i\times i^j\)

对于区间修改在预处理时做前缀和,直接赋值

显然正整数幂和组合数都能预处理,

所以时间复杂度\(O((n+m)klogn)\)

卡常:

  1. 不用快速幂,预处理的时候通过\(i^{j-1}\)推出\(i^j\)
  2. 注意0^0=1,虽然在初中无意义
  3. 线段树每次只递归1次,\(j=0\sim 5\)同时修改查询,查询最后用一个结构体记录答案,懒标记只需要1个
  4. 组合数可以不用阶乘,实测快30ms

代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100011,mod=1000000007;
int si[6][N],a[N],n,m,c[6][6];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline void Pro(int n){
	c[0][0]=1;
	for (rr int i=1;i<6;++i){
		c[i][0]=c[i][i]=1;
		for (rr int j=1;j<i;++j)
		    c[i][j]=c[i-1][j-1]+c[i-1][j];
	}
	for (rr int i=0;i<=n;++i) si[0][i]=1;
	for (rr int j=1;j<6;++j)
	for (rr int i=1;i<=n;++i) si[j][i]=1ll*si[j-1][i]*i%mod;
	for (rr int j=0;j<6;++j)
	for (rr int i=1;i<=n;++i) si[j][i]=mo(si[j][i],si[j][i-1]);
}
inline signed Get(int k,int l,int r){return mo(si[k][r],mod-si[k][l-1]);}
struct Array_Six{int Q[6];};
struct Segment_Tree{
	int w[N<<2][6],lazy[N<<2];
	inline void pup(int k){
		for (rr int j=0;j<6;++j)
		    w[k][j]=mo(w[k<<1][j],w[k<<1|1][j]);
	}
	inline void build(int k,int l,int r){
		lazy[k]=-1;
		if (l==r){
			for (rr int j=0;j<6;++j)
			    w[k][j]=1ll*a[l]*Get(j,l,l)%mod;
		    return;
		}
		rr int mid=(l+r)>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		pup(k);
	}
	inline void pdown(int k,int l,int mid,int r){
		if (~lazy[k]){
			lazy[k<<1]=lazy[k<<1|1]=lazy[k];
			for (rr int j=0;j<6;++j)
			    w[k<<1][j]=1ll*lazy[k]*Get(j,l,mid)%mod,
			    w[k<<1|1][j]=1ll*lazy[k]*Get(j,mid+1,r)%mod;
			lazy[k]=-1;
		}
	}
	inline void update(int k,int l,int r,int x,int y,int z){
		if (l==x&&r==y){
			lazy[k]=z;
			for (rr int j=0;j<6;++j)
			    w[k][j]=1ll*z*Get(j,l,r)%mod;
			return;
		}
		rr int mid=(l+r)>>1; pdown(k,l,mid,r);
		if (y<=mid) update(k<<1,l,mid,x,y,z);
		else if (x>mid) update(k<<1|1,mid+1,r,x,y,z);
		else update(k<<1,l,mid,x,mid,z),update(k<<1|1,mid+1,r,mid+1,y,z);
		pup(k);
	}
	inline Array_Six query(int k,int l,int r,int x,int y){
		rr Array_Six t1,t2;
		if (l==x&&r==y){
			for (rr int j=0;j<6;++j)
			    t1.Q[j]=w[k][j];
			return t1;
		}
		rr int mid=(l+r)>>1; pdown(k,l,mid,r);
		for (rr int j=0;j<6;++j) t1.Q[j]=t2.Q[j]=0;
		if (y<=mid) t1=query(k<<1,l,mid,x,y);
		else if (x>mid) t2=query(k<<1|1,mid+1,r,x,y);
		else t1=query(k<<1,l,mid,x,mid),
		    t2=query(k<<1|1,mid+1,r,mid+1,y);
		for (rr int j=0;j<6;++j) t1.Q[j]=mo(t1.Q[j],t2.Q[j]);
		return t1;
	}
}Tre;
signed main(){
	n=iut(); m=iut(); Pro(N-11);
	for (rr int i=1;i<=n;++i) a[i]=iut();
	Tre.build(1,1,n);
	for (;m;--m){
		rr char C=getchar();
		while (C!='?'&&C!='=') C=getchar();
		if (C=='='){
			rr int l=iut(),r=iut(),x=iut();
			Tre.update(1,1,n,l,r,x);
		}else{
			rr int l=iut(),r=iut(),k=iut(),ans=0;
			rr Array_Six t=Tre.query(1,1,n,l,r);
			for (rr int j=0,W;j<=k;++j){
				if (l>1) W=Get(k-j,l-1,l-1);
				    else W=si[k-j][l-1];
				if (((k^j)&1)&&W) W=mod-W;
				ans=mo(ans,1ll*W*c[k][j]%mod*t.Q[j]%mod);
			}
			print(ans),putchar(10);
		}
	}
	return 0;
}
posted @ 2020-02-25 21:53  lemondinosaur  阅读(205)  评论(0编辑  收藏  举报