http://livearchive.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3809

单点更新+区间查询+维护区间公式和

这题算是昨天开始刷线段树做过的比较难的了吧....首先要把维护量想出来, 再把合并区间的方式想出来, 然后写的时候 还有很多要注意的地方......


注: 思考的时候一定要明确一点, 让每个区间都满足一个性质, 合并结果的时候, 面对的都是满足这个性质的子区间. 合并出来的也是满足这个性质的.


三个地方被卡了(T_T.........):

1/ 在query的时候, 左右区间合并结果也要按公式算.而不是直接相加.

2/ 在query的时候, 如果并不是左右区间都占, 则 不能按 公式算.....写的时候要分开, 或者 判断.

3/ 在query的时候, 右区间长度并不是 (r-mid) , 而是 (R-mid)........R-mid 才是查询 的 真实的长度...


代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
inline int Rint() { int x; scanf("%d", &x); return x; }
inline int max(int x, int y) { return (x>y)? x: y; }
inline int min(int x, int y) { return (x<y)? x: y; }
#define FOR(i, a, b) for(int i=(a); i<=(b); i++)
#define FORD(i,a,b) for(int i=(a);i>=(b);i--)
#define REP(x) for(int i=0; i<(x); i++)
typedef long long int64;
#define INF (1<<30)
const double eps = 1e-8;
#define bug(s) cout<<#s<<"="<<s<<" "

//	单点更新, 区间查询,	维护 ....一个恶心的式子的求和

#define MAXN 100002
int a[MAXN<<2];
int fac[MAXN];
int B, P, L, N;
void pushup(int l, int r, int e)
{
	int mid =  (l+r)>>1;
	int cnt = (r-mid);
	//int fac = 1;
	//FOR(k, 1, cnt) fac*=B;
	//a[e] = (int)(((int64)a[e>>1]*fac+a[e<<1|1])%P);
	a[e] = (int)(((int64)a[e<<1]*fac[cnt]+a[e<<1|1])%P);
	//bug(a[e<<1]);bug(fac[cnt]);bug(a[e<<1|1])<<endl;
}
//void build()
void update(int p, int v, int l, int r, int e)
{
	if(l==r)
		a[e] = v;
	else
	{
		int mid = (l+r)>>1;
		if(p<=mid)
			update(p, v, l, mid, e<<1);
		else
			update(p, v, mid+1,  r,  e<<1|1);
		pushup(l, r, e);
	}
}
int query(int L, int R, int l, int r, int e)
{
	if(L<=l && r<=R)
		return a[e];
	else
	{
		int mid = (l+r)>>1;
		int flag1=0, flag2=0;
		int ret = 0;
		if(L<=mid)
		{
			ret=query(L, R, l, mid, e<<1);			//这里也是合并子区间!!!! 也要按 那个公式, 而不是简单 相加
			flag1=1;
		}
		//ret=(int)(((int64)ret*fac[r-mid])%P);												//如果是单个区间就不用合并!!!!!		很tricky啊 
		int ret2 = 0;
		if(mid+1<=R)
		{
			ret2=query(L, R, mid+1, r, e<<1|1);
			flag2 = 1;
		}

		if(flag1 && flag2)
			//ret = (int)(((int64)ret*fac[r-mid]+ret2)%P);
			ret = (int)(((int64)ret*fac[R-mid]+ret2)%P);		//是R-mid不是r-mid.... R是真正查询的区间.....恩
		else
			ret+=ret2;

		//	也 可以这样, 直接 判断是左右都占还是单单占一边.
		//int64  res, res1,res2, tmp;
		//if(R<=mid)
		//	res=query(L,R,l,mid,e<<1);
		//else if(mid<L)
		//	res=query(L,R,mid+1,r,e<<1|1);
		//else
		//{
		//	res1=query(L,mid,l,mid,e<<1);
		//	res2=query(mid+1,R,mid+1,r,e<<1|1);
		//	int Len=R-mid;
		//	res=res1*fac[Len]+res2;
		//	if(res>=P)res%=P;
		//}
		//return (int)res;
		return ret;
	}
}
bool read()
{
	scanf("%d%d%d%d", &B, &P, &L, &N);
	return B+P+L+N != 0;
}
int main()
{
	while(read())
	{
		//bug(L)<<endl;
		memset(a, 0, sizeof(a));
		fac[0] = 1;
		FOR(i, 1, L)
		{
			fac[i]=(int)(((int64)fac[i-1]*B)%P);
			//bug(fac[i])<<endl;
		}
		REP(N)
		{
			char  ch[2];
			scanf("%s",ch); char op=ch[0];
			int x = Rint();
			int y =Rint();
			if(op == 'E')
				update(x, y, 1, L, 1);
			else
				printf("%d\n", query(x, y, 1, L, 1));
		}
		puts("-");
	}
}