您的位置:首页 > 其它

5798 - Jupiter Atacks!

2012-08-12 11:56 260 查看
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("-");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: