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\)见祖宗