题意是给你i,j,求H( fi,..., fj) = Bkfj-k ( mod P)然后fi会时时更新。
做法:思路很清楚,明显是线段树或者树状数组。那个不过那个乘以b^k怎么处理?其实很容易,一开始加进树状数组里就把第一个的f0乘以b^(L-1),f1乘以b^(L-2)。。以此类推,因为那个b的次方是以此递减的,然后要算 i 到 j就把i-j的所有数加起来除以b^(L-j)就可以了,但是这里有个问题,因为数据非常大必须边算边取模,所以取模的除法是不行的,要用到模M的乘法逆元,因为p是素数,根据费马小定理,a^(p-1)%p = 1,那么a^(p-2)*a%p = 1,即a^(p-2)是a的逆元(要用快速幂取模),然后就可以递推求其他次方的逆元了。
AC代码:
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<ctime>
#include<string.h>
#include<string>
using namespace std;
#define ll long long
#define eps 1e-8
template<class T>
inline void scan_d(T &ret)
{
char c;
ret=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
}
ll T[100005];
ll bp[100005],p,l;
ll cal(int x, int n)
{
ll i=x,ans = 1;
while(n)
{
if(n&1)
{
ans *= i%p;
ans %= p;
}
i = (i%p)*(i%p);
i %= p;
n >>= 1;
}
return ans;
}
int lowbit(int x)
{
return x&(-x);
}
ll sum(int x)
{
ll ret = 0;
while(x > 0)
{
ret += T[x];
ret %= p;
x -= lowbit(x);
}
return ret;
}
void add(int x,ll d)
{
while(x <= l)
{
T[x] += d;
T[x] %= p;
x += lowbit(x);
}
}
ll a[100005],ni[100005];
int main()
{
#ifdef GLQ
freopen("input.txt","r",stdin);
// freopen("o2.txt","w",stdout);
#endif // GLQ
int b,n,i;
while(~scanf("%d%lld%lld%d\n",&b,&p,&l,&n) && b+p+l+n)
{
memset(T,0,sizeof(T));
memset(a,0,sizeof(a));
bp[0] = 1;
for(i = 1; i < l; i++)
bp[i] = ((bp[i-1]%p)*(b%p))%p;
ll t = cal(b,p-2);
ni[0] = 1;
for(i = 1; i < l; i++)
ni[i] = ((ni[i-1]%p)*(t%p))%p;
for(i = 1; i <= n; i++)
{
ll x,y;
char temp;
scanf("%c %d %d\n",&temp,&x,&y);
if(temp == 'E')
{
y = (y%p)*(bp[l-x]%p);
y %= p;
if(y < 0) y += p;
// cout<<"haha "<<y<<endl;
add(x,y-a[x]);
a[x] = y;
}
if(temp == 'H')
{
ll temp = cal(b,l-y);
ll ans = sum(y) - sum(x-1);
ans = ((ans%p)*(ni[l-y]%p))%p;
if(ans < 0) ans += p;
printf("%lld\n",ans);
}
}
printf("-\n");
}
return 0;
}