链接:http://icpc.upc.edu.cn/problem.php?cid=1803&pid=13
题意:给出一个n,接下来一行给出n个数。才开始所有数为0,每次操作可以选一个区间[l,r]使得区间内的数全部+1。问最少的操作次数得到这个序列,并且输出每步的区间。
思路:线段树维护区间内的最小数以及下标。枚举左端点,右端点肯定越大越优。每次先找出a[i]剩下多少,再迭代的从[i,n]区间中找可行的最靠右的最小值(假设下标为r),每次更新[i,r]的值,直到a[i]减为0。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
int n,a[N],minval,minpos;
struct Node
{
int l,r,val,pos,lazy;
//val:[l,r]中最小的值,pos是下标
//lazy:val的该变量
}tree[N<<2];
//答案
struct node
{
int l,r,num;
node(){}
node(int l,int r,int num):l(l),r(r),num(num){}
}ans[N];
void pushup(int cur)
{
tree[cur].val=min(tree[cur<<1].val,tree[cur<<1|1].val);
if(tree[cur<<1].val<=tree[cur<<1|1].val)
tree[cur].pos=tree[cur<<1].pos;
else
tree[cur].pos=tree[cur<<1|1].pos;
return ;
}
//区间更新值,lazy标记
void pushdown(int cur)
{
if(tree[cur].lazy)
{
tree[cur<<1].lazy+=tree[cur].lazy;
tree[cur<<1].val+=tree[cur].lazy;
tree[cur<<1|1].lazy+=tree[cur].lazy;
tree[cur<<1|1].val+=tree[cur].lazy;
tree[cur].lazy=0;
}
return ;
}
void build(int l,int r,int cur)
{
tree[cur].l=l;
tree[cur].r=r;
tree[cur].lazy=0;
if(l==r)
{
tree[cur].val=a[l];
tree[cur].pos=l;
return ;
}
int m=(l+r)>>1;
build(l,m,cur<<1);
build(m+1,r,cur<<1|1);
pushup(cur);
return;
}
void update(int l,int r,int cur,int val)
{
if(l<=tree[cur].l&&tree[cur].r<=r)
{
tree[cur].val+=val;
tree[cur].lazy+=val;
return ;
}
pushdown(cur);
if(l<=tree[cur<<1].r) update(l,r,cur<<1,val);
if(r>=tree[cur<<1|1].l) update(l,r,cur<<1|1,val);
pushup(cur);
return ;
}
int query(int l,int r,int cur)
{
int res=N;
if(l<=tree[cur].l&&tree[cur].r<=r)
{
//更新该区间的最小值以及下标
if(tree[cur].val<=minval)
{
minval=tree[cur].val;
minpos=tree[cur].pos;
}
return tree[cur].val;
}
pushdown(cur);
//先查左子树再查右子树,保证是最右的可行端点。
if(l<=tree[cur<<1].r) res=query(l,r,cur<<1);
if(r>=tree[cur<<1|1].l) res=min(res,query(l,r,cur<<1|1));
pushup(cur);
return res;
}
int main(void)
{
int cnt=0,sum=0,val,r;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,n,1);
for(int l=1;l<=n;l++)
{
minval=N;
val=query(l,l,1);
if(!val) continue;
r=n;//尽可能往右,这样次数才更少,答案更优。
//迭代改变a[l]的值
while(val)
{
minval=N;
query(l,r,1);
val-=minval;
if(minval)//minval如果为0,该答案不用记
{
sum+=minval,ans[cnt++]=node(l,r,minval);
update(l,r,1,-minval);
}
//minpos的数已变为0,再从[l,minspos-1]找最小值。
r=minpos-1;
}
}
printf("%d\n",sum);
for(int i=0;i<cnt;i++)
{
for(int j=0;j<ans[i].num;j++)
printf("%d %d\n",ans[i].l,ans[i].r);
}
return 0;
}