题解:P14306 【MX-J27-T3】旋律
qdl66666666 · · 题解
一篇线段树题解
题意
给你一个序列,你可以选择其中一些数,定义这个子序列的和谐度为区间长度乘
让你选出一个子序列,使得和谐度最大,输出这个最大值。
思路
转化1
考虑子序列不好做,我们进行排序,这样我们一定选一个区间作为答案。
考虑证明:对于
还有一个显然的结论,一个区间的最大值和最小值放在了这个区间的两端。
转化2
那根据类似扫描线的思想,先枚举一维,再用数据结构维护另一维。
这里我们枚举区间右端点
我们考虑加入一个右端点会使前面的数如何更新和谐度。
设原区间长度为
所以我们每添加一个数,就将线段树中维护的和谐度都加
实现
维护区间最大值,支持区间加的线段树。
具体:对序列排序,枚举每个位置,对全局加
注意多测清空。
#include<bits/stdc++.h>
#define ll long long
#define ls rt<<1
#define rs rt<<1|1
#define mid ((l+r)>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
using namespace std;
const int N = 2e5 + 10;
template<typename TY>
inline void read(TY &s){
ll x = 0,f = 1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
s = x * f;
}
int c,T;
int n;
ll k,ans;
ll maxn[N<<2],tag[N<<2],a[N];
inline void push_up(int rt){
maxn[rt] = max(maxn[ls],maxn[rs]);
}
void build(int rt,int l,int r){
maxn[rt] = 0; tag[rt] = 0;
if(l == r) return;
build(lson); build(rson);
}
inline void add(int rt,int l,int r,ll v){
maxn[rt] += v; tag[rt] += v;
}
inline void push_down(int rt,int l,int r){
if(!tag[rt]) return;
add(lson,tag[rt]); add(rson,tag[rt]);
tag[rt] = 0;
}
void modify(int rt,int l,int r,int ql,int qr,ll v){
if(ql <= l && r <= qr){
add(rt,l,r,v);
return;
}
push_down(rt,l,r);
if(ql <= mid) modify(lson,ql,qr,v);
if(mid + 1 <= qr) modify(rson,ql,qr,v);
push_up(rt);
}
void update(int rt,int l,int r,int x,ll v){
if(l == r){
maxn[rt] = v;
return;
}
push_down(rt,l,r);
if(x <= mid) update(lson,x,v);
else update(rson,x,v);
push_up(rt);
}
int main(){
// freopen("melody4.in","r",stdin);
read(c); read(T);
while(T--){
read(n); read(k);
for(int i=1;i<=n;i++){
read(a[i]);
}
sort(a+1,a+n+1);
build(1,1,n);
ans = 0;
for(int i=1;i<=n;i++){
modify(1,1,n,1,n,k - a[i] + a[i-1]);
update(1,1,n,i,k);
ans = max(ans,maxn[1]);
}
cout << ans << "\n";
}
return 0;
}