uvalive5798(树状数组)

题意:

单点更新,区间求和,求和操作和普通的求和不一样,如果我们要求2~5的和,sum=1*f[5]+b*f[4]+b*b*f[3]+b*b*b*f[2],输出每个询问操作的答案,同时答案要模p。


思路:
我们可以在操作的时候直接给每个位置的数字成上b的多少次幂,例如如果是五个数字,我们插入f1时,我们不插入f1,而是插入f1*b*b*b*b,插入f2时,我们插入f2*b*b*b,这样,询问的时候我们就可以直接区间求和,再除以一个b的多少次幂。同时要注意,除法取模要求逆元,计算b的次幂的时候要用快速幂,被红书的快速幂坑了。。。它的那个取模不够彻底。


代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>

using namespace std;

long long _b,_p,_l,_n;

long long pow_mod(long long a,long long i,long long n)
{
	if(i==0)
		return 1%n;
	int temp=pow_mod(a%n,i>>1,n)%n;
	temp=(temp%n*temp%n)%n;
	if(i&1)
		temp=(long long)temp*a%n;
	return temp;
}

long long extend_gcd(long long a,long long b,long long &x,long long &y)  
{  
    if(a==0&&b==0) return -1;//无最大公约数  
    if(b==0){x=1;y=0;return a;}  
    long long d=extend_gcd(b,a%b,y,x);  
    y-=a/b*x;  
    return d;  
}  

long long mod_reverse(long long a,long long n)  
{  
    long long x,y;  
    long long d=extend_gcd(a,n,x,y);  
    if(d==1) return (x%n+n)%n;  
    else return -1;  
}  

const long long maxn=100005;
long long Tree[maxn+10];
inline long long lowbit(long long x)
{
	return x&(-x);
}

void add(long long x,long long value)
{
	for(long long i=x;i<=maxn;i+=lowbit(i))
	{
		Tree[i]+=value;
		Tree[i]%=_p;
	}
}

long long get(long long x)
{
	long long sum=0;
	for(long long i=x;i;i-=lowbit(i))
	{
		sum+=Tree[i];
		sum%=_p;
	}
	return (sum);
}

int main()
{
	//cout << pow_mod(2, 10, 1000000) << endl;
	while(scanf("%lld%lld%lld%lld",&_b,&_p,&_l,&_n)!=EOF)
	{
		memset(Tree,0,sizeof Tree);
		if(!_b&&!_p&&!_l&&!_n)
			return 0;
		for(long long i=1;i<=_n;i++)
		{
			char s[3];
			long long x,y;
			scanf("%s%lld%lld",s,&x,&y);
			if(s[0]=='E')
			{
				y%=_p;
				long long tmp=(get(x)-get(x-1)+_p)%_p;
			//	printf("tmp=%lld\n",tmp);
			//	printf("lala=%lld\n",(y*pow_mod(_b,_l-x,_p)%_p-tmp+_p)%_p);
				add(x,(y*pow_mod(_b%_p,_l-x,_p)%_p-tmp+_p)%_p);
				//add(x,y);
			}
			else
			{
				long long ans=(get(y)%_p-get(x-1)%_p+_p)%_p;
			//	printf("---------------- %lld %lld \n",get(y),get(x-1));
			//	printf("chu=%lld\n",pow_mod(_b%_p,_l-y,_p));
			//	printf("ans=%lld\n",ans);
				ans%=_p;
				long long ni=mod_reverse(pow_mod(_b%_p,_l-y,_p),_p);
				ni%=_p;
				ans*=ni;
				printf("%lld\n",ans%_p);
			}
		}
		printf("-\n");
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值