扶桑号战列舰
传送门 来源upc:12800
题目描述
众所周知,一战过后,在世界列强建造超无畏级战列舰的竞争之中,旧日本海军根据“个舰优越主义”,建造了扶桑级战列舰,完工时为当时世界上武装最为强大的舰只。
同时,扶桑号战列舰也是舰岛最为科幻的战列舰。
当然,要建造这样的舰船,科技水平是必须的。
同样众所周知的是,德意志科学技术天下第一,所以IJN的司令官从德国学来了一种先进的建船方法。
一只战舰横过来可以看做一个长度为n的序列,每个位置有一个数ai表示这个位置设计的高度。这种先进的造船技术可以每次将一个区间[l,r]内的所有位置高度都+1,求到达最终设计状态的最少操作次数。
如果你不能及时完成的话,IJN司令官会奖励你去参加苏里高海战。
输入
第一行包含一个整数n,表示序列的长度。
第二行包含n个非负整数a1,a2,a3,…,an,表示最终的状态。
输出
输出的第一行是一个正整数m,表示最少的操作次数。
接下来m行每行两个正整数li,ri,表示一次操作。
你需要保证1≤li≤ri≤n。
保证最少次数m≤105,输出可以以任意顺序输出。
样例输入
6
2 3 3 3 3 3
样例输出
3
1 6
1 6
2 6
提示
思路:(直接说不太好理解,看例子)
输入:
8
4 5 6 5 4 2 10 4
1、输入的值当作高,用树状图将每个点表示出来:
2、用单调栈维护出左区间即左边第一个比它小的位置,右区间同理:(单调栈维护:传送门)
x=1: [1,5] h=4
x=2: [2,4] h=5
... ... ...
x=8: [7,8] h=4
3、将上面的区间按照对应点的高从小到大去重并排序,。
h=2 [1,8]
h=4 [1,5]
h=4 [7,8]
... ...
h=10 [7,7]
之所以还有两个4没被去重,是因为它们左右区间不同。
4、还原柱状图:
step1: 根据求得的区间,一层一层的将高度增加,增加后用flag数组记录这个点现在的高度。
(h=2 [1,8] )
step2: 由step1可知flag[1]=2,但第二个h=4,所以这个点的高度要再加2,加完后flag[1]要更新为4,这一步我试了用fill()来维护更新,但结果T了,所以要用树状数组(感谢:Wintob 的板子)。
(h=4 [1,5] )
step3: 同上
(h=4 [7,8] )
step4:同上
(h=5 [2,4] )
step5:同上
(h=6 [3,3] )
step6:同上
(h=10 [7,7] )
就得到了原来的柱形图。
具体输出看代码怎么实现的,不懂欢迎留言。
AC代码:(代码长是因为没想到其他好的办法维护flag数组,用树状数组更快但会长一些)
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5;
int n;
int a[MAX+5];
int top;
int Stack[MAX+5];
struct node1{
int l;
int r;
int cnt; ///区间输出几次
}num[MAX+5];
struct node{
int dir;
int l;
int r;
}edge[MAX+5],edge1[MAX+5];
bool cmp(node x,node y)
{
return x.dir<y.dir;
}
struct node2
{
int l,r;
long long sum,poi;
}tree[4040400];
int m;
int c,l,r,x;
long long d;
long long a1[1010100];
void build(int p,int l,int r) // 建树 ///树状数组直接粘的板子,只是用来维护tree数组的
{
tree[p].l=l;tree[p].r=r;
if (l==r) {
tree[p].sum=a1[l];
return;
}
int mid=(l+r)/2;
if (l<=mid) build(p*2,l,mid);
if (r>mid) build(p*2+1,mid+1,r);
tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;
}
void spread(int p)
{
if (tree[p].poi)
{
tree[p*2].sum+=(tree[p*2].r-tree[p*2].l+1)*tree[p].poi;
tree[p*2+1].sum+=(tree[p*2+1].r-tree[p*2+1].l+1)*tree[p].poi;
tree[p*2].poi+=tree[p].poi;
tree[p*2+1].poi+=tree[p].poi;
tree[p].poi=0;
}
}
void change(int p,int l,int r,long long d)// 区间修改
{
if (l<=tree[p].l&&tree[p].r<=r)
{
tree[p].sum+=(tree[p].r-tree[p].l+1)*d;
tree[p].poi+=d;
return;
}
spread(p);
int mid=(tree[p].l+tree[p].r)/2;
if (l<=mid) change(p*2,l,r,d);
if (r>mid) change(p*2+1,l,r,d);
tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;
}
long long ask(int p,int l,int r) //区间查询
{
if (l>tree[p].r||r<tree[p].l) return 0;
if (l<=tree[p].l&&tree[p].r<=r)
return tree[p].sum;
spread(p);
int mid=(tree[p].l+tree[p].r)/2;
long long val=0;
if (l<=mid) val+=ask(p*2,l,r);
if (r>mid) val+=ask(p*2+1,l,r);
return val;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
edge[i].dir=a[i];
}
a[0]=-1,a[n+1]=-1;
Stack[top]=0;
for(int i=1;i<=n;i++){
while(a[Stack[top]]>=a[i]) top--;
edge[i].l=Stack[top]+1;
Stack[++top]=i;
}
top=0;
Stack[top]=n+1;
for(int i=n;i>=1;i--){
while(a[Stack[top]]>=a[i]) top--;
edge[i].r=Stack[top]-1;
Stack[++top]=i;
}
sort(edge+1,edge+n+1,cmp);
int cnt=0;
for(int i=1;i<=n;i++){
if(!((edge[i].l==edge[i-1].l)&&(edge[i].l==edge[i-1].l)&&(edge[i].dir==edge[i-1].dir))){
edge1[cnt].dir=edge[i].dir;
edge1[cnt].l=edge[i].l;
edge1[cnt++].r=edge[i].r;
}
}
int sum=0,cnt2=0; ///sum是最后的总次数
build(1,1,n);
for(int i=0;i<cnt;i++){
int num1=edge1[i].dir-ask(1,edge1[i].l,edge1[i].l); ///计算还需要建多高
num[cnt2].l=edge1[i].l,num[cnt2].r=edge1[i].r,num[cnt2++].cnt=num1;
sum+=num1;
change(1,edge1[i].l,edge1[i].r,num1); ///更新tree数组
}
printf("%d
",sum);
for(int i=0;i<cnt2;i++){
for(int j=0;j<num[i].cnt;j++){
printf("%d %d
",num[i].l,num[i].r);
}
}
return 0;
}