风信子(超级钢琴带修)解题报告
前言
前置trick:超级钢琴
打模拟赛遇上这个,如果知道trick,很容易往超级钢琴上想,但又有不同之处,遂爆零。
为了纪念第一次自己改出T4,于是作此篇报告。
题目描述
定义一个合法二元组 \((i,j)\) 需要满足 \(i\),\(j\) 为整数,且 \(i≤j\)。如果 \(i>j\),则 \((i,j)\)不构成合法二元组。
定义一个合法二元组 (i,j)(i,j) 的分数为 \(a_i-a_j\)。
定义一个合法二元组 \((i,j)\) 在区间 \([l,r]\) 内,当且仅当 \(l≤i,j≤r\)。
有 \(m\) 次操作:
1 l r x:表示将序列中第 \(l\) 个位置到第 \(r\) 个位置都加上 \(x\)。
2 l r k:表示询问选出 \(k\) 个不同的合法二元组,每个合法二元组都在区间 \([l,r]\)内,这些合法二元组的分数和最高是多少。
关联与区别
超级钢琴的 trick 就是用堆来维护可选答案集合,对于每个堆顶还可以再分成范围更小的答案集合,取出\(k\)次堆顶就可以求出答案。
与之类似的还有可持久化0/1trie的一道题:异或粽子
回到这题,我们会发现这道题带修,这好说,用线段树来维护,只不过将查询复杂度由之前st表的\(O(1)\)变为\(O(logn)\)(无妨,这题数据范围小一点,给的时限也长)。
除此之外,在超级钢琴中我们固定一个端点,另一端为一个区间,但在这题,左右端点都在一个区间里,这个处理起来便会有些复杂。
对答案集合的操作
因为两端都是区间的原因,所以我们要找到一个可以便捷求答案的左右区间,分别是:(设左端点可选区间为\([l_1,r_1]\),右端点可选区间为\([l_2,r_2]\))
- \(l_1=l_2\),\(r_1=r_2\) : 就是求区间最大差值(用线段树维护)
- \(r_1<l_2\) : 就是两区间无交集,答案为左区间最大值减去右区间最小值,还可以用线段树维护
由上,我们在将大的答案集合分成若干小的答案集合,也应分成上面这两种,最开始的区间满足第一种情况,接下来的“分裂”应是这样:
设当前左端点的可选区间为\([l_1,r_1]\),右端点可选区间为\([l_2,r_2]\),最优答案的左端点为\(x\),右端点为\(y\)(线段树上维护,根据上面的方法可以很好求出来),当然因为第一种情况下\(l_1=l_2,r_1=r_2\),这里不再区分。
对于第一种情况(同一区间)
下一次最优答案的左右端点可能在下面这几种情况:

左右端点均在\([l,x-1]\)内,可选条件:\(x>l\)

左端点在\([l,x-1]\),右端点在\([x,r]\)内,可选条件:\(x>l\)

左端点不动,右端点也在左端点处,可选条件:\(x\neq y\)

左端点不动,右端点在\([x+1,y-1]\)内,可选条件:\(x<y-1\)

左端点不动,右端点在\([y+1,r]\)内,可选条件:\(y<r\)

左右端点均在\([x+1,r]\)处,可选条件:\(x<r\)
观察发现,我们其实是在对左端点进行分类讨论,随着左端点可选区间的右移,右端点可选区间也在变化。
对于第二种情况(无交区间)
还是讨论下一次最优答案的可选区间,以左端点为标准。

左端点在\([l_1,x-1]\),右端点在\([l_2,r_2]\),可选条件:\(x>l_1\)

左端点不动,右端在\([l_2,y-1]\)内,可选条件:\(l_2<y\)

左端点不动,右端点在\([y+1,r_2]\)内,可选条件:\(y<r_2\)

左端点在\([x+1,r_1]\),右端点在\([l_2,r_2]\),可选条件:\(x<r1\)
至此关键步骤已经解决,接下来难点在于线段树维护
线段树要干啥
综合上述,不难发现,答案查询以及带修操作均需要线段树,因此线段树要维护这些:
pair<int,int> max//最大值以及他的位置
pair<int,int> min//最小值以及他的位置
int val//区间最大差值
int x,y//区间最大差值的左右端点
int tag//区修懒标记
对应的\(pushup\)与\(query\)皆要变化,可以通过代码理解。
代码(写的有点长)
点击查看代码
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<tuple>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn=1e5+5;
#define int long long
int n,m,a[maxn];
struct nd
{
int l,r,tag;
pair<int,int> ma,mi;
int val;
int val_L,val_R;
}v[maxn<<2];//线段树变量
void pushup(int rt)
{
v[rt].ma=max(v[lson].ma,v[rson].ma);
v[rt].mi=min(v[lson].mi,v[rson].mi);
//对区间最大差值进行分讨
if(v[lson].val>=v[rson].val&&v[lson].val>=v[lson].ma.first-v[rson].mi.first)
{
v[rt].val=v[lson].val;
v[rt].val_L=v[lson].val_L;
v[rt].val_R=v[lson].val_R;
}
else if(v[rson].val>=v[lson].val&&v[rson].val>=v[lson].ma.first-v[rson].mi.first)
{
v[rt].val=v[rson].val;
v[rt].val_L=v[rson].val_L;
v[rt].val_R=v[rson].val_R;
}
else
{
v[rt].val=v[lson].ma.first-v[rson].mi.first;
v[rt].val_L=v[lson].ma.second;
v[rt].val_R=v[rson].mi.second;
}
}
void pushdown(int rt)
{
if(v[rt].tag==0) return;
v[lson].ma.first+=v[rt].tag;
v[lson].mi.first+=v[rt].tag;
v[rson].ma.first+=v[rt].tag;
v[rson].mi.first+=v[rt].tag;
v[lson].tag+=v[rt].tag;
v[rson].tag+=v[rt].tag;
v[rt].tag=0;
}
void build(int rt,int l,int r)
{
v[rt].l=l,v[rt].r=r;
if(l==r)
{
v[rt].ma={a[l],l};
v[rt].mi={a[l],l};
v[rt].val_L=v[rt].val_R=l;
v[rt].val=0;
return;
}
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(rt);
}
void update(int rt,int L,int R,int val)
{
if(v[rt].l>=L&&v[rt].r<=R)
{
v[rt].ma.first+=val;
v[rt].mi.first+=val;
v[rt].tag+=val;
return;
}
pushdown(rt);
int mid=(v[rt].l+v[rt].r)>>1;
if(L<=mid) update(lson,L,R,val);
if(R>mid) update(rson,L,R,val);
pushup(rt);
}
nd query(int rt,int L,int R)//返回结构体,用啥取啥
{
if(v[rt].l>=L&&v[rt].r<=R) return v[rt];
pushdown(rt);
int mid=(v[rt].l+v[rt].r)>>1;
if(R<=mid) return query(lson,L,R);
else if(L>mid) return query(rson,L,R);
else
{
nd v1=query(lson,L,R),v2=query(rson,L,R);
nd ret={};
ret.ma=max(v1.ma,v2.ma);
ret.mi=min(v1.mi,v2.mi);
if(v1.val>=v2.val&&v1.val>=v1.ma.first-v2.mi.first)
{
ret.val=v1.val;
ret.val_L=v1.val_L;
ret.val_R=v1.val_R;
}
else if(v2.val>=v1.val&&v2.val>=v1.ma.first-v2.mi.first)
{
ret.val=v2.val;
ret.val_L=v2.val_L;
ret.val_R=v2.val_R;
}
else
{
ret.val=v1.ma.first-v2.mi.first;
ret.val_L=v1.ma.second;
ret.val_R=v2.mi.second;
}
return ret;
}
}
struct zz//堆里的元素
{
int l1,r1,l2,r2,val,pos_x,pos_y;
bool operator <(const zz &vv) const
{
return val<vv.val;
}
};
priority_queue<zz> q;
int solve(int l,int r,int k)
{
int ret=0;
while(!q.empty()) q.pop();
nd op=query(1,l,r);
q.push({l,r,l,r,op.val,op.val_L,op.val_R});
while(k&&!q.empty())
{
zz gh=q.top();
q.pop();
ret+=gh.val;
k--;
if(gh.l1==gh.l2 && gh.r1==gh.r2)//第一种情况
{
if(gh.pos_x>gh.l1)
{
nd df=query(1,gh.l1,gh.pos_x-1);
q.push({gh.l1,gh.pos_x-1,gh.l1,gh.pos_x-1,df.val,df.val_L,df.val_R});
nd jk=query(1,gh.pos_x,gh.r1);
q.push({gh.l1,gh.pos_x-1,gh.pos_x,gh.r1,df.ma.first-jk.mi.first,df.ma.second,jk.mi.second});
}
if(gh.pos_x!=gh.pos_y)
{
q.push({gh.pos_x,gh.pos_x,gh.pos_x,gh.pos_x,0,gh.pos_x,gh.pos_x});
}
if(gh.pos_x<gh.pos_y-1)
{
nd df=query(1,gh.pos_x,gh.pos_x);
nd jk=query(1,gh.pos_x+1,gh.pos_y-1);
q.push({gh.pos_x,gh.pos_x,gh.pos_x+1,gh.pos_y-1,df.ma.first-jk.mi.first,df.ma.second,jk.mi.second});
}
if(gh.pos_y<gh.r1)
{
nd df=query(1,gh.pos_x,gh.pos_x);
nd jk=query(1,gh.pos_y+1,gh.r1);
q.push({gh.pos_x,gh.pos_x,gh.pos_y+1,gh.r1,df.ma.first-jk.mi.first,df.ma.second,jk.mi.second});
}
if(gh.pos_x<gh.r1)
{
nd df=query(1,gh.pos_x+1,gh.r1);
q.push({gh.pos_x+1,gh.r1,gh.pos_x+1,gh.r1,df.val,df.val_L,df.val_R});
}
}
else if(gh.r1 < gh.l2) // 第二种情况
{
if(gh.pos_x>gh.l1)
{
nd df=query(1,gh.l1,gh.pos_x-1);
nd jk=query(1,gh.l2,gh.r2);
q.push({gh.l1,gh.pos_x-1,gh.l2,gh.r2,df.ma.first-jk.mi.first,df.ma.second,jk.mi.second});
}
if(gh.l2<gh.pos_y)
{
nd df=query(1,gh.pos_x,gh.pos_x);
nd jk=query(1,gh.l2,gh.pos_y-1);
q.push({gh.pos_x,gh.pos_x,gh.l2,gh.pos_y-1,df.ma.first-jk.mi.first,df.ma.second,jk.mi.second});
}
if(gh.pos_y<gh.r2)
{
nd df=query(1,gh.pos_x,gh.pos_x);
nd jk=query(1,gh.pos_y+1,gh.r2);
q.push({gh.pos_x,gh.pos_x,gh.pos_y+1,gh.r2,df.ma.first-jk.mi.first,df.ma.second,jk.mi.second});
}
if(gh.pos_x<gh.r1)
{
nd df=query(1,gh.pos_x+1,gh.r1);
nd jk=query(1,gh.l2,gh.r2);
q.push({gh.pos_x+1,gh.r1,gh.l2,gh.r2,df.ma.first-jk.mi.first,df.ma.second,jk.mi.second});
}
}
else cout<<"666 ";//调试
}
return ret;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
freopen("D.in","r",stdin);
freopen("D.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
for(int i=1,op,l,r,k;i<=m;i++)
{
cin>>op>>l>>r>>k;
if(op==1) update(1,l,r,k);
else cout<<solve(l,r,k)<<'\n';
}
return 0;
}

浙公网安备 33010602011771号