5798 - Jupiter Atacks!

本文深入探讨了一道复杂的线段树题目,详细介绍了如何解决单点更新、区间查询及维护区间公式的难点。通过实例代码讲解了实现过程中的关键步骤与易错点。

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


### JUnit-Jupiter 和 JUnit-Jupiter-API 的主要区别 JUnit 5 是一个模块化的测试框架,其核心由多个模块组成,其中 `junit-jupiter` 和 `junit-jupiter-api` 是两个重要的模块。以下是它们之间的主要区别: #### ### 1. 模块定义与功能划分 - **JUnit-Jupiter-API** `junit-jupiter-api` 是 JUnit Jupiter 的 API 模块,提供了编写测试所需的所有注解和断言方法。它包含诸如 `@Test`、`@BeforeEach`、`@AfterEach` 等注解以及断言工具类(如 `Assertions` 和 `Assumptions`)。这个模块仅提供 API,不包含运行测试的逻辑[^1]。 - **JUnit-Jupiter** `junit-jupiter` 是一个聚合模块,包含了 `junit-jupiter-api` 和 `junit-jupiter-engine`。它是一个方便的依赖项,用于同时引入 API 和引擎,使得开发者可以轻松地编写和运行测试[^1]。 #### ### 2. 测试运行支持 - **JUnit-Jupiter-API** 单独引入 `junit-jupiter-api` 时,可以编写测试代码,但无法直接运行测试,因为缺少测试引擎的支持。测试引擎负责实际执行测试用例[^1]。 - **JUnit-Jupiter** 引入 `junit-jupiter` 后,不仅能够编写测试,还可以通过内置的 `junit-jupiter-engine` 来运行测试。这意味着它是一个完整的解决方案,既包括 API 又包括运行时支持。 #### ### 3. 依赖关系 - **JUnit-Jupiter-API** 它是 `junit-jupiter` 的一部分,但本身并不依赖于其他模块。因此,如果只需要编写测试而不运行,可以选择单独使用 `junit-jupiter-api`。 - **JUnit-Jupiter** 它依赖于 `junit-jupiter-api` 和 `junit-jupiter-engine`,是一个更高层次的抽象,适合大多数开发场景[^1]。 #### ### 示例 Maven 配置 以下是使用 `junit-jupiter-api` 和 `junit-jupiter` 的 Maven 配置示例: ```xml <!-- 仅引入 API --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.3</version> <scope>test</scope> </dependency> <!-- 引入完整模块 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.3</version> <scope>test</scope> </dependency> ``` #### ### 示例代码对比 以下展示了如何使用 `junit-jupiter-api` 编写测试,以及需要 `junit-jupiter-engine` 才能运行测试: ```java // 使用 junit-jupiter-api 编写的测试 import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class ExampleTest { @Test public void testAddition() { assertEquals(2, 1 + 1, "1 + 1 should equal 2"); } } ``` 如果没有引入 `junit-jupiter-engine`,上述测试将无法运行,因为缺少执行测试的逻辑[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值