【洛谷P4231】三步必杀【差分】

本文探讨了一种高效算法,通过差分和线段树技术解决序列更新问题,实现O(n+m)时间复杂度下求解序列的异或和及最大值。关键在于两次差分操作消除公差,简化计算。

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P4231
一个序列,初始值全部为0。每次修改一段区间 [ l , r ] [l,r] [l,r],区间 [ l , r ] [l,r] [l,r]中第 i i i个元素加上等差数列的第 i i i项。求最终数列的异或和以及最大值。


思路:

n ≤ 1 0 7 n\leq 10^7 n107。连带 l o g log log的数据结构都不给过。
由于每次加的是一个等比数列,所以考虑使用差分来解决这个问题。
假设原数列为 a a a,差分数列为 b b b

编号123456789
a a a000000000
b b b000000000

这次在 [ 3 , 6 ] [3,6] [3,6]中加入等差数列,首项加 x x x,莫项加 y y y,公差为 d d d

编号123456789
a a a00 x x x x + d x+d x+d x + 2 d x+2d x+2d x + 3 d ( y ) x+3d(y) x+3d(y)000
b b b00 x x x d d d d d d d d d − x − 3 d ( − y ) -x-3d(-y) x3d(y)00

那么现在就是一个单点修改+区间修改的问题了。用线段树时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)
这样的复杂度还是不够优秀,如果可以把区间修改去掉,那么就可以直接在数列上单点修改了。
那么就在 b b b数组上再进行一次差分,这样就可以把 d d d消掉。

编号123456789
a a a00 x x x x + d x+d x+d x + 2 d x+2d x+2d y y y000
b b b00 x x x d d d d d d d d d − y -y y00
c c c00 x x x d − x d-x dx00 − y − d -y-d yd y y y0

所以我们每次修改时单点修改4个点就可以了。
那么考虑如何用 c c c数组推出最终的 a a a数组。
根据差分,有 a i = ∑ j = 1 i ∑ x k = 1 j c x k a_i=\sum^{i}_{j=1}\sum^{j}_{xk=1}c_{xk} ai=j=1ixk=1jcxk。这个 ∑ x k = 1 j c x k \sum^{j}_{xk=1}c_{xk} xk=1jcxk可以把 c x k c_{xk} cxk做一个前缀和,然后易得 a i = a i − 1 + s u m i a_i=a_{i-1}+sum_i ai=ai1+sumi。这样就 O ( n + m ) O(n+m) O(n+m)完成了这道题。


代码:

#include <cstdio>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=1e7+10;
int n,m,l,r;
ll a[N],sum[N],add,x,y,ans1,ans2;

ll read()
{
	ll d=0;
	char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch))
		d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

int main()
{
	n=(int)read(); m=(int)read();
	for (int i=1;i<=m;i++)
	{
		l=(int)read(); r=(int)read(); x=read(); y=read();
		add=(y-x)/(ll)(r-l);
		a[l]+=x;
		a[l+1]+=add-x;
		a[r+1]-=add+y;
		a[r+2]+=y;
	}
	for (int i=1;i<=n;i++)
	{
		sum[i]=sum[i-1]+a[i];
		a[i]=a[i-1]+sum[i];
		ans1^=a[i];
		ans2=max(ans2,a[i]);
	}
	printf("%lld %lld\n",ans1,ans2);
	return 0;
}
### 洛谷 P4231 三步必杀 树状数组 解题思路 对于给定的四个数 \( A, B, C, D \),目标是计算满足条件 \( A \leq x \leq B \leq y \leq C \leq z \leq D \) 的三角形数量。此问题可以通过树状数组(Binary Indexed Tree, BIT)来高效解决。 #### 使用树状数组的原因 树状数组能够支持高效的区间查询和单点更新操作,在处理此类涉及范围统计的问题时非常有用[^1]。 #### 数据预处理 为了简化后续逻辑,可以先对输入数据进行去重并离散化处理,使得数值映射到较小范围内连续整数上,从而减少空间复杂度。 #### 主要算法流程 核心思想在于枚举中间边长度作为固定值 `y` ,利用前缀和技巧快速获取小于等于当前选定 `y` 值的数量以及大于等于 `C-y+1` (即另一条较短边最小可能取值)的数量之差,以此得到合法组合数目。 具体实现如下: ```cpp #include <bits/stdc++..h> using namespace std; const int MAXN = 50005; int n, a[MAXN], b[MAXN]; long long bit[MAXN]; // 更新函数用于向BIT中入新元素 void update(int idx, int val){ while(idx <= n){ bit[idx] += val; idx += (-idx)&idx; // lowbit运算找到下一个需要修改的位置 } } // 查询函数返回[1,x]区间的累积频率 long long query(int idx){ long long sum=0; while(idx>0){ sum+=bit[idx]; idx-idx)&idx; } return sum; } ``` 通过上述代码片段实现了基本的树状数组功能,包括更新(`update`)与查询(`query`)两个主要接口。接下来就是如何应用这些工具解决问题的关键部分了。 针对题目描述中的约束条件\(A\leq x\leq B\)、\(B\leq y\leq C\) 及 \(C\leq z\leq D\),当遍历每一个潜在的第二条边长`y`时,只需考虑那些能构成有效三角形的第一条边`x`及其对应的第三条边`z`即可。这一步骤依赖于之前提到过的前缀和概念——即预先计算好所有可能出现的情况,并存储起来供即时检索使用。 最后需要注意边界情况的处理,比如当三条边完全相等的时候也要计入结果之中;另外由于题目要求的是严格意义上的不同三角形形态,因此还需要排除掉重复计数的情形。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值