codechef Chef and Churu

做法:考虑对函数进行分块(1-根号n,根号n+1-根号2*n,之类的函数)

我们可以通过差分预处理出每个块中的函数分别对应几个a[i]之和

即sigma(a[i]*k[i])中的系数k[i],然后预处理出每个块的和记为sum[i]

考虑修改操作,显然很简单我们可以直接改一下每个块的sum值

然后考虑查询操作,整块的随便做,但是零散的反而麻烦了

所以用树状数组记录一下a的前缀和就行了

唯一的坑点:long long好像不够,要开unsigned long long才行

代码:

#include<bits/stdc++.h>
#define N 500005
#define lll unsigned long long
using namespace std;
lll ll[N],rr[N],a[N],c[N],l[N],r[N],cnt[405][N/5+5],sum[405];
lll n,Q,opt,x,y,block,num; 
inline lll gt(lll x){return (x-1)/block+1;}
inline lll lowbit(lll x){return x&-x;}
inline void add(lll x,lll y){for(lll i=x;i<=n;i+=lowbit(i))c[i]+=y;}
inline lll gtsum(lll x){lll ans=0;for(lll i=x;i;i-=lowbit(i))ans+=c[i];return ans;}
inline void change(lll x,lll y){
	add(x,y-a[x]);
	for (lll i=1;i<=num;i++)
		sum[i]+=cnt[i][x]*(y-a[x]);
	a[x]=y;
}
inline lll query(lll x,lll y){
	lll ans=0;
	if (gt(x)==gt(y)||gt(x)+1==gt(y))
		for (lll i=x;i<=y&&i<=n;i++) ans+=gtsum(r[i])-gtsum(l[i]);
	else {
		for (lll i=gt(x)+1;i<gt(y);i++) ans+=sum[i];
		for (lll i=x;i<=rr[gt(x)]&&i<=n;i++) ans+=gtsum(r[i])-gtsum(l[i]);
		for (lll i=ll[gt(y)];i<=y&&i<=n;i++) ans+=gtsum(r[i])-gtsum(l[i]);
	}
	return ans;
}
int main(){
	scanf("%llu",&n);block=(lll)sqrt(n);
	for (lll i=1;i<=n;i++) scanf("%llu",&a[i]),add(i,a[i]);
	num=n/block;if (n%block) num++;
	for (lll i=1;i<=num;i++) ll[i]=(i-1)*block+1,rr[i]=i*block;rr[num]=n;
	for (lll i=1;i<=n;i++) scanf("%llu%llu",&l[i],&r[i]),l[i]--;
	for (lll i=1;i<=num;i++){
		for (lll j=ll[i];j<=rr[i];j++){
			cnt[i][l[j]+1]++;
			cnt[i][r[j]+1]--;
		}
	}
	for (lll i=1;i<=num;i++){
		for (lll j=1;j<=n;j++){
			cnt[i][j]+=cnt[i][j-1];
			sum[i]=sum[i]+cnt[i][j]*a[j];
		}
	}
	//printf("%d\n",cnt[2][4]);
	scanf("%llu",&Q);
	while (Q--){
		scanf("%llu%llu%llu",&opt,&x,&y);
		if (opt==1) change(x,y);
		else printf("%llu\n",query(x,y));
	}
	return 0;
} 

  

猜你喜欢

转载自www.cnblogs.com/ckr1225/p/9010098.html