题目
题意: 给定n个点,每两个相邻点之间有数的限制。现在,有q次询问。每次询问可以将区间[l+1,r]内的数 - 区间内最小值,然后ans + 此值。ans最大是多少。询问可以打乱顺序后处理。
思路: 线段树维护最小值+tag区间更新即可。
时间复杂度: O(nlogn)
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
#define int long long
int n,m,k,T;
int a[N];
int ans[N<<2];
int tag[N<<2];
#define ls p<<1
#define rs p<<1|1
typedef long long ll;
struct node{
int l,r;
bool operator<(const node&rhs)const
{
return r < rhs.r;
}
}q[N];
inline void push_up(int p)
{
ans[p] = min(ans[ls],ans[rs]);
}
void build(int p,int l,int r)
{
tag[p] = 0;
if(l == r)
{
ans[p] = a[l];
return ;
}
int mid = l+r>>1;
build(ls,l,mid),build(rs,mid+1,r);
push_up(p);
}
inline void f(int p,int l,int r,int k)
{
ans[p] += k;
tag[p] += k;
}
inline void push_down(int p,int l,int r)
{
if(tag[p])
{
int mid = l+r>>1;
f(ls,l,mid,tag[p]);
f(rs,mid+1,r,tag[p]);
tag[p] = 0;
}
}
inline void update(int p,int l,int r,int nl,int nr,int k)
{
if(l >= nl && r <= nr)
{
tag[p] += k;
ans[p] += k;
return ;
}
push_down(p,l,r);
int mid = l+r>>1;
if(nl<=mid) update(ls,l,mid,nl,nr,k);
if(nr>mid) update(rs,mid+1,r,nl,nr,k);
push_up(p);
}
int query(int p,int l,int r,int nl,int nr)
{
if(l >= nl && r<=nr)
{
return ans[p];
}
push_down(p,l,r);
int mid = l+r>>1;
int res = 1e18;
if(nl<=mid) res = min(res,query(ls,l,mid,nl,nr));
if(nr>mid) res = min(res,query(rs,mid+1,r,nl,nr));
return res;
}
void solve()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
n--;
for(int i=1;i<=n;++i) cin>>a[i];
build(1,1,n);
// for(int i=1;i<=n;++i) cout<<i<<":"<<ans[i]<<endl;
ll ans = 0;
for(int i=0;i<m;++i)
{
int l,r; cin>>l>>r;
if(l > r) swap(l,r);
l++;
q[i].l = l,q[i].r = r;
}
sort(q,q+m);
for(int i=0;i<m;++i)
{
int l = q[i].l,r = q[i].r;
int mx = query(1,1,n,l,r);
ans += mx;
// cout<<l<<" "<<r<<":"<<mx<<"?"<<endl;
update(1,1,n,l,r,-mx);
}
cout<<ans;
}
signed main(void)
{
solve();
return 0;
}