【题解】[COCI2020-2021#5] Sjeckanje

solution:

数据结构毒瘤题。

算法一:考虑不带修改。首先分析出一个性质,最优区间一定是单调区间。因为 f(l,r)≤max(l,r)−min(l,r)f(l,r)\leq max(l,r)-min(l,r)f(l,r)max(l,r)min(l,r) ,如果一个区间不是单调区间的话可以继续拆分直到都是单调区间,从而使得 f(l,r)≥max(l,r)−min(l,r)f(l,r)\geq max(l,r)-min(l,r)f(l,r)max(l,r)min(l,r) 。设 f[i]f[i]f[i] 表示前 iii 个数贡献的最大值,可以 O(n)O(n)O(n) 求出,区间 (l,r](l,r](l,r] 的贡献就是 a[r]−a[l+1]a[r]-a[l+1]a[r]a[l+1] 。加上修改的话就暴力修改,时间复杂度 O(n2)O(n^2)O(n2)

算法二:考虑维护差分数组,将区间修改转化为单点修改。现在对于单调区间 [l,r][l,r][l,r] ,其贡献等于 ∑i=lrc[i]\sum_{i=l}^rc[i]i=lrc[i] 。进一步地,总贡献等于 ∑i=1nc[i]\sum_{i=1}^nc[i]i=1nc[i] ,所以我们只需要维护差分数组的和即可。规定 c[1]=0c[1]=0c[1]=0 。当然实际计算是求的绝对值的和。

一个坑点在于,len=1len=1len=1 的情况是没有贡献的,比较难判断。所以我们必须调整我们的策略:转化为在差分序列中选择一些数,使得选择的数中不存在相邻两个数,一个为正,一个为负。证明也比较容易,考虑 c[i]c[i]c[i] 能产生贡献当且仅当 [i,i+1][i,i+1][i,i+1] 在同一区间,如果有两个相邻的线段不能在同一集合的话就不能都产生贡献,反之则能。

现在我们可以用线段树维护了。dp[t][0/1][0/1]dp[t][0/1][0/1]dp[t][0/1][0/1] 表示节点 ttt 的 左/右 端点 不选/选 时贡献的最大值。时间复杂度 O(24(n+q)logn)O(2^4(n+q)logn)O(24(n+q)logn)

#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int mx=2e5+5;
int n,q;
ll dp[mx<<2][2][2];
ll a[mx],c[mx];
void PushUp(int p,int md) {
	for(int i=0;i<2;i++) {
		for(int j=0;j<2;j++) {
			dp[p][i][j]=-INF;
			for(int k=0;k<2;k++) {
				for(int l=0;l<2;l++) {
					if(k==1&&l==1&&(c[md]<0&&c[md+1]>0||c[md]>0&&c[md+1]<0)) continue;
					dp[p][i][j]=max(dp[p][i][j],dp[p<<1][i][k]+dp[p<<1|1][l][j]); 
				}
			}
		}
	}
}
void update(int p,int l,int r,int x) {
	if(l==r) {
		dp[p][1][1]=abs(c[l]);
		return;
	}
	int mid=l+r>>1;
	if(x<=mid) update(p<<1,l,mid,x);
	else update(p<<1|1,mid+1,r,x);
	PushUp(p,mid);
}
ll query() {
	return max(max(dp[1][0][0],dp[1][0][1]),max(dp[1][1][0],dp[1][1][1]));
}
int main() {
//	freopen("data.in","r",stdin);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),c[i]=(i==1)?0:a[i]-a[i-1],update(1,1,n,i);
	for(int i=1;i<=q;i++) {
		int l,r,x; scanf("%d%d%d",&l,&r,&x);
		if(l!=1) {
			c[l]+=x;
			update(1,1,n,l);
		}
		if(r!=n) {
			c[r+1]-=x;
			update(1,1,n,r+1);
		}
		printf("%lld\n",query());
	}
}
``` #include<bits/stdc++.h> using namespace std; int n,a[100005]; long long ans=0; int main(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=2;i<=n;i++)if(a[i]>a[i-1])ans+=a[i]-a[i-1]; cout<<ans+a[1]<<endl; return 0; }```好的,我现在需要帮用户完善这个题目的题解,他们已经提供了代码和问题描述。首先,我得仔细理解这个问题和代码的逻辑。 用户给出的代码看起来是一个解决积木大赛问题的方案。题目要求用最少的操作次数让积木达到指定高度。每次操作可以选一个区间,让区间内的所有积木高度加1。代码的关键部分是在遍历数组时,累加相邻两个积木中后面比前面高的差值,最后再加上第一个积木的高度。 首先,我需要确认这个算法的正确性。为什么相邻差值能决定操作次数?假设当前积木比前一个高,那么这部分多出来的高度必须通过新的操作来完成,因为这些操作不能由前面的积木的操作覆盖。例如,如果前一个积木已经处理完了,当前积木更高的部分需要独立的操作,比如从当前位置开始的区间。 然后,题解需要结构清晰,包括题目描述、输入输出、样例解释、算法思路、正确性证明、代码解析等部分。用户可能希望题解能详细解释为什么这个方法是正确的,特别是差分的思想。 接下来,我需要考虑用户可能的疑问点。例如,为什么首项要单独加?因为第一个积木的高度无法被之前的操作覆盖,只能通过从第一个位置开始的操作来实现。另外,对于非递增的部分,比如后面的积木比前面低,为什么不需要处理?因为前面的操作已经覆盖了这些部分,不需要额外操作。 还需要验证一下样例输入是否正确。比如样例输入是5个积木,高度2、3、4、1、2。相邻差值有3-2=1,4-3=1,1-4是负数不计,2-1=1,总和是3,加上首项2,得到5,和样例输出一致。这说明代码是正确的。 在写题解时,需要分步骤解释思路,可能从暴力法入手,说明为什么暴力法效率低,然后引出差分或贪心的策略。然后详细说明如何通过比较相邻元素来计算所需操作次数,以及首项的处理。 最后,确保题解结构符合用户的要求,使用正确的LaTeX格式,并且用中文清晰表达。同时,检查是否有遗漏的部分,比如数据范围的说明,或者时间复杂度分析,这样可以帮助读者理解为什么这个方法适用于大规模数据。 $\huge\textbf{题解:积木大赛}$ --- ### **题目分析** 我们需要用最少的区间加法操作,将初始全零的积木阵列构建成目标高度。每次操作可以选择任意连续区间进行整体+1。 --- ### **算法核心:差分思想** #### **关键观察** 1. **递增段的必要性**:当当前积木高度 $h_i$ 大于前一块 $h_{i-1}$ 时,差值 $(h_i - h_{i-1})$ 必须通过新的操作覆盖(因为这些高度无法通过之前的区间操作叠加得到) 2. **首块的独立性**:第一块积木的高度必须完全由覆盖它的操作产生 #### **数学表达** - 总操作次数 = 首块高度 + $\sum_{i=2}^{n} \max(0, h_i - h_{i-1})$ --- ### **代码实现** ```cpp #include<bits/stdc++.h> using namespace std; int n, a[100005]; long long ans = 0; int main() { cin >> n; for(int i = 1; i <= n; i++) cin >> a[i]; // 计算所有递增差值的和 for(int i = 2; i <= n; i++) if(a[i] > a[i-1]) ans += a[i] - a[i-1]; // 加上首块独立高度 cout << ans + a[1] << endl; return 0; } ``` --- ### **正确性证明** 1. **基本单元分解**:将目标高度分解为多个垂直层: - 每个层对应一次连续区间操作 - 层数即为操作次数 2. **横向扩展分析**: - 当 $h_i > h_{i-1}$ 时,必须新增 $(h_i - h_{i-1})$ 层从 $i$ 开始的右向操作 - 当 $h_i \leq h_{i-1}$ 时,这些层数已被前面积木的操作覆盖 --- ### **复杂度分析** - **时间复杂度**:$O(n)$ 仅需一次遍历数组 - **空间复杂度**:$O(n)$ 存储高度数组 --- ### **示例解析** 以样例输入 `5 2 3 4 1 2` 为例: 1. 首块高度 $h_1 = 2$ 2. 递增差值: - $3-2=1$(第2块) - $4-3=1$(第3块) - $2-1=1$(第5块) 3. 总操作数:$2 + (1+1+1) = 5$ --- ### **拓展思考** 1. **反向验证**:如果最后一块高度为0,如何处理? 2. **变形题目**:若允许每次操作增减任意数值,问题将如何变化? 3. **工程应用**:该算法思想可应用于资源分配优化、图像渲染等场景 --- $\boxed{\text{该算法通过巧妙的差分处理,将复杂问题转化为线性扫描,展现了贪心思想的精妙}}$继续完善这个题解,把数学建模的过程也呈现出来
最新发布
04-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值