P4231 三步必杀

P4231 三步必杀

大致题意

给一个初值全为\(0\)的数列,有\(m\)次修改操作都将一个给定区间\([l,r]\)加上首相为\(s\)末项为\(e\)的等差数列

求:所有修改完成后所有数的异或和跟最大值

分析

算法一

通过观察可以发现,对于差分数组\(b\),除了\(l\)\(r+1\)位置,其他位置的值都相等

考虑用线段树记录差分数组,每次修改时,将:

  • 区间\([l+1,r]\)加上一个公差\(d\)

  • \(l\)位置加上一个首相\(s\)

  • \(r+1\)位置减去一个\(d×(r-l)+s\)

单点修改\(+\)区间修改\(+\)区间查询

可以获得38\(pts\)

算法二

区间\([l+1,r]\)的值均为\(d\),考虑对数组\(b\)再进行一次差分

\(c\)\(b\)的差分数组

每次修改时将\(:\)

  • \(c_l\)的值加上首相\(s\)

  • \(c_{l+1}\)的值加上\(d -s\)

  • \(c_{r+1}\)的值减去\(d - e\)

  • \(c_{r+2}\)的值加上\(e\)

即可

只需要修改四个元素

对于每个\(a_x\),其值即为\(\sum_{i=1}^x\sum_{j=1}^{i}c_j\)

\(code:\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1000010;
#define int long long
int a[MAXN] , n ,m,ans[MAXN];
int ans1,ansmaxn;
signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int l,r,s,e;
		scanf("%d%d%d%d",&l,&r,&s,&e);
		a[l] += s;
		a[l+1]+=(((e-s)/(r-l)) -s);
		a[r+1]+=-(((e-s)/(r-l))+e);
		a[r+2]+=e;
	}
	for(int i=1;i<=n;i++) a[i]+=a[i-1];
	for(int i=1;i<=n;i++){
		ans[i] = ans[i-1]+a[i];
		ans1^=ans[i];
		ansmaxn = max(ansmaxn , ans[i]);
	}
	cout<<ans1<<" "<<ansmaxn;
}

坑点&&感想

  • 当原差分数组有特殊性质时,考虑二次差分

  • 不开\(long long\)见祖宗

posted @ 2020-09-05 22:17  xcxc82  阅读(135)  评论(0编辑  收藏  举报