CC NOV14 Chef and Churu 分块+BIT维护单点.

题意:长度为n的序列a,现在有n个函数f,第i个函数f[i]的值为(a[l[i]]+a[l[i]+1]...a[r[i]]).Q个操作
操作1:修改a[i]的值.
操作2:求f[l]+f[l+1]+..f[r]的值.
n,Q<=1e5.a[i]<=1e9.


可以用BIT O(logn)内求出一个f[i]的值 查询一次要O(nlogn) 总共O(Q*nlogn) TLE....
现在能在O(logn)内求出一个f[i].考虑对序列f进行分块.


用差分对每一块进行预处理,处理出这一块中每个a[i]出现的次数,同时维护该块的f[i]之和. 


对于修改 知道序列a中第p个位置在该块的出现次数,容易更新出该块f[i]之和 O(sqrt(n)).
对于查询,头尾两个非整块BIT暴力求出f[i] 中间块O(1)累加.O(sqrt(n)*logn+sqrt(n)).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=450;
ll m,n,num,Q,L[N],R[N],c[N],pos[N];
ll a[N];
unsigned long long val[N],b[N],cnt[M][N];
int lowbit(int x)
{
	return x&-x;
}
void update(int x,ll val)
{
	for(int i=x;i<N;i+=lowbit(i))
		b[i]+=val;
}
ll sum(int x)
{
	ll res=0;
	for(int i=x;i>0;i-=lowbit(i))
		res+=b[i];
	return res;
}
void init()
{
	m=(int)sqrt(n);
	num=n/m;
	if(n%m)
		num++;
	for(int i=1;i<=n;i++)
		pos[i]=(i-1)/m+1;
	for(int i=1;i<=num;i++)
	{
		memset(c,0,sizeof(c));	
		for(int j=(i-1)*m+1;j<=i*m&&j<=n;j++)
		{
			c[L[j]]++;
			c[R[j]+1]--;
		}
		for(int k=1;k<=n;k++)
			c[k]+=c[k-1],cnt[i][k]=c[k],val[i]+=c[k]*a[k];
	}
}
ll ask(int l,int r)
{
	ll res=0;
	if(pos[l]==pos[r])
	{
		for(int i=l;i<=r;i++)
			res+=sum(R[i])-sum(L[i]-1);
	}
	else
	{
		for(int i=l;i<=pos[l]*m;i++)
			res+=sum(R[i])-sum(L[i]-1);
		for(int i=(pos[r]-1)*m+1;i<=r;i++)
			res+=sum(R[i])-sum(L[i]-1);
	}
	for(int i=pos[l]+1;i<pos[r];i++)
		res+=val[i];
	return res;
}
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]),update(i,a[i]);
	for(int i=1;i<=n;i++)
		scanf("%lld%lld",&L[i],&R[i]);
	init();
	scanf("%lld",&Q);
	ll op,l,r,p,x;
	while(Q--)
	{
		scanf("%lld",&op);	
		if(op==1)
		{
			scanf("%lld%lld",&p,&x);
			for(int i=1;i<=num;i++)
			{
				ll t=cnt[i][p];
				val[i]=val[i]+t*(x-a[p]);
			}
			update(p,x-a[p]);
			a[p]=x;
		}
		else
		{
			scanf("%lld%lld",&l,&r);
			printf("%llu\n",ask(l,r));
		}
	}
	return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值