题目
题意
给定
n
n
个数的序列,定义两个操作
把序列第k个数的值变为val
⋅1lrk
⋅
1
l
r
k
询问在区间
Al⋯Ar
A
l
⋯
A
r
中,选取
m
m
段不相交的子区间,使得这 段子区间的和最大,其中
0≤m≤k
0
≤
m
≤
k
。
思路
首先这里讲了如何解决
k=1
k
=
1
的情况,即用线段树维护并进行合并
blablabla....
b
l
a
b
l
a
b
l
a
.
.
.
.
然后考虑如何取k段,我们只需要去给定区间取
k
k
次区间最大子段和,累加答案后,对取出的区间做区间取反操作。而当所取的区间最大子段和小于 的时候,则跳出不计
做法的正确性可以从贪心或者费用流的思想上证明
之后再进行单点修改就行了
那么实现的就在维护的是
maxs,maxr,maxl
m
a
x
s
,
m
a
x
r
,
m
a
x
l
这三个值的时候,如何进行取反操作,如何在找出
maxs
m
a
x
s
的同时记录下,对应的哪一段区间。(这三个变量的含义见区间最大子段和部分)
而取反实际上就是将最大子段和以及最小子段和进行了交换,而我们只需要同时维护最大子段和最小子段和,记录下对应的区间信息,在取反操作时对调就可以了。而又由于我们的
maxs
m
a
x
s
是用
maxr,maxl
m
a
x
r
,
m
a
x
l
来维护的,所有后两者的区间信息也需要记录
于是就有了这么一个维护了18个值的线段树…
sum
s
u
m
区间和
[l, r]
[
l
,
r
]
区间的左右边界
maxs
m
a
x
s
最大子段和
[maxsl, maxsr]
[
m
a
x
s
l
,
m
a
x
s
r
]
最大子段和的左右边界
mins
m
i
n
s
最小子段和
[maxsl, maxsr]
[
m
a
x
s
l
,
m
a
x
s
r
]
最小子段和的左右边界
maxl
m
a
x
l
最大前缀和
maxlp
m
a
x
l
p
最大前缀和的右边界
minl
m
i
n
l
最小前缀和
minlp
m
i
n
l
p
最小前缀和的右边界
maxr
m
a
x
r
最大后缀和
maxrp
m
a
x
r
p
最大后缀和的左边界
minr
m
i
n
r
最小后缀和
minrp
m
i
n
r
p
最小后缀和的左边界
tag
t
a
g
区间取反标记
然后将区间合并更新所有的最佳情况函数化为了
merge( )
m
e
r
g
e
(
)
又由于使用了
merge
m
e
r
g
e
,所以
tag
t
a
g
需要在
merge
m
e
r
g
e
前记录一下并赋值
其他的就是基本的线段树的操作了
代码
#include <bits/stdc++.h>
using namespace std;
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d%d",&n,&m)
#define sddd(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define pd(n) printf("%d\n", (n))
#define pdd(n,m) printf("%d %d", n, m)
#define pld(n) printf("%lld\n", n)
#define pldd(n,m) printf("%lld %lld\n", n, m)
#define sld(n) scanf("%lld",&n)
#define sldd(n,m) scanf("%lld%lld",&n,&m)
#define slddd(n,m,k) scanf("%lld%lld%lld",&n,&m,&k)
#define sf(n) scanf("%lf",&n)
#define sff(n,m) scanf("%lf%lf",&n,&m)
#define sfff(n,m,k) scanf("%lf%lf%lf",&n,&m,&k)
#define ss(str) scanf("%s",str)
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define mm(a,n) memset(a, n, sizeof(a))
#define debug(x) cout<<#x<<": "<<x<<endl
#define pb push_back
#define mk make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
typedef pair<int,int> PII;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const ll mod = 1000000007;
const double eps = 1e-9;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+5;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
template<typename T>inline void read(T &x){
T f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(x=0;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
x*=f;
}
// head
struct Node {
int l, r;
int sum, tag;
int maxs, maxl, maxr;
int maxsl, maxsr, minsl, minsr;
int maxlp, maxrp, minlp, minrp;
int mins, minl, minr;
} tree[maxn<<2];
int num[maxn];
Node merged(Node ls, Node rs){
Node ret;
ret.sum = ls.sum + rs.sum;
ret.l = ls.l;
ret.r = rs.r;
ret.tag = 0;
if(ls.maxs > rs.maxs){
ret.maxs = ls.maxs;
ret.maxsl = ls.maxsl;
ret.maxsr = ls.maxsr;
}
else {
ret.maxs = rs.maxs;
ret.maxsl = rs.maxsl;
ret.maxsr = rs.maxsr;
}
if(ret.maxs < ls.maxr + rs.maxl) {
ret.maxs = ls.maxr + rs.maxl;
ret.maxsl = ls.maxrp;
ret.maxsr = rs.maxlp;
}
if(ls.maxl > ls.sum + rs.maxl){
ret.maxl = ls.maxl;
ret.maxlp = ls.maxlp;
}
else{
ret.maxl = ls.sum + rs.maxl;
ret.maxlp = rs.maxlp;
}
if(rs.maxr > rs.sum + ls.maxr){
ret.maxr = rs.maxr;
ret.maxrp = rs.maxrp;
}
else {
ret.maxr = rs.sum + ls.maxr;
ret.maxrp = ls.maxrp;
}
if(ls.mins < rs.mins){
ret.mins = ls.mins;
ret.minsl = ls.minsl;
ret.minsr = ls.minsr;
}
else {
ret.mins = rs.mins;
ret.minsl = rs.minsl;
ret.minsr = rs.minsr;
}
if(ret.mins > ls.minr + rs.minl) {
ret.mins = ls.minr + rs.minl;
ret.minsl = ls.minrp;
ret.minsr = rs.minlp;
}
if(ls.minl < ls.sum + rs.minl){
ret.minl = ls.minl;
ret.minlp = ls.minlp;
}
else{
ret.minl = ls.sum + rs.minl;
ret.minlp = rs.minlp;
}
if(rs.minr < rs.sum + ls.minr){
ret.minr = rs.minr;
ret.minrp = rs.minrp;
}
else {
ret.minr = rs.sum + ls.minr;
ret.minrp = ls.minrp;
}
return ret;
}
void inv(int i){
tree[i].sum = -tree[i].sum;
tree[i].tag ^= 1;
swap(tree[i].maxs, tree[i].mins);
swap(tree[i].maxsl, tree[i].minsl);
swap(tree[i].maxsr, tree[i].minsr);
tree[i].maxs = -tree[i].maxs; tree[i].mins = -tree[i].mins;
swap(tree[i].maxl, tree[i].minl);
swap(tree[i].maxlp, tree[i].minlp);
tree[i].maxl = -tree[i].maxl; tree[i].minl = -tree[i].minl;
swap(tree[i].maxr, tree[i].minr);
swap(tree[i].maxrp, tree[i].minrp);
tree[i].maxr = -tree[i].maxr; tree[i].minr = -tree[i].minr;
}
void push_down(int i){
if(tree[i].tag){
//cout<<"pd "<<i<<endl;
inv(i<<1);
inv(i<<1|1);
tree[i] = merged(tree[i<<1], tree[i<<1|1]);
tree[i].tag = 0;
}
}
void build(int i, int l, int r) {
tree[i].l = l;
tree[i].r = r;
tree[i].tag = 0;
if(l == r) {
tree[i].sum = tree[i].maxs = tree[i].mins = num[l];
tree[i].maxl = tree[i].maxr = num[l];
tree[i].minl = tree[i].minr = num[l];
tree[i].maxsl = tree[i].maxsr = l;
tree[i].minsl = tree[i].minsr = l;
tree[i].maxlp = tree[i].maxrp = l;
tree[i].minlp = tree[i].minrp = l;
return ;
}
int mid = (l+r)>>1;
build(i<<1, l, mid);
build(i<<1|1, mid+1, r); //子树建树后更新特征值
int tag = tree[i].tag;
tree[i] = merged(tree[i<<1], tree[i<<1|1]);
tree[i].tag = tag;
return ;
}
Node query(int i, int l, int r) {
if(tree[i].l==l && tree[i].r==r)
return tree[i];
push_down(i);
int mid = (tree[i].l+tree[i].r)>>1;
if(mid >= r)
return query(i<<1, l, r);
else if(mid < l)
return query(i<<1|1, l, r);
else {
Node ls = query(i, l, mid);
Node rs = query(i, mid+1, r);
Node ans = merged(ls, rs);
return ans;
}
}
void setk(int i, int k, int val){
if(tree[i].l == tree[i].r){
if(tree[i].tag) val = -val;
tree[i].sum = tree[i].maxs = tree[i].mins = val;
tree[i].maxl = tree[i].maxr = val;
tree[i].minl = tree[i].minr = val;
return ;
}
push_down(i);
int mid = (tree[i].l+tree[i].r)>>1;
if(k <= mid)
setk(i<<1, k, val);
else setk(i<<1|1, k, val);
int tag = tree[i].tag;
tree[i] = merged(tree[i<<1], tree[i<<1|1]);
tree[i].tag = tag;
}
void settag(int i, int l, int r) {
if(tree[i].l==l && tree[i].r==r){
inv(i);
return ;
}
push_down(i);
int mid = (tree[i].l+tree[i].r)>>1;
if(mid >= r)
settag(i<<1, l, r);
else if(mid < l)
settag(i<<1|1, l, r);
else {
settag(i<<1, l, mid);
settag(i<<1|1, mid+1, r);
}
tree[i] = merged(tree[i<<1], tree[i<<1|1]);
}
int lc[maxn], rc[maxn];
int main()
{
int n, m, op, l, r, k, val;
sd(n);
rep(i, 1, n+1)
sd(num[i]);
build(1, 1, n);
sd(m);
rep(i, 0, m){
sd(op);
if(op) {
int sum = 0, cnt = 0;
sddd(l, r, k);
rep(j, 0, k){
Node now = query(1, l, r);
if(now.maxs < 0) break;
sum += now.maxs;
settag(1, now.maxsl, now.maxsr);
lc[cnt] = now.maxsl;
rc[cnt++] = now.maxsr;
}
pd(sum);
rep(j, 0, cnt)
settag(1, lc[j], rc[j]);
}
else {
sdd(k, val);
setk(1, k, val);
}
}
return 0;
}
小结
merge
m
e
r
g
e
函数枚举情况写的有点丑..但是一时半会儿没有想到有什么很好的优化办法
在每次操作后将线段树还原,需要把取反区间再取反一边。如果是遍历线段树重置的话会
T
T
<script type="math/tex" id="MathJax-Element-155">T</script>