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("-"); } }