数据结构一
复健\(Day5\)
数据结构一
\(1.\)单调栈和单调队列
\((1).\)海报\(PLA-Postering\)
https://www.luogu.com.cn/problem/P3467
首先很明显宽度是无用的因素,直接忽略掉即可。
对于海报覆盖,我们发现只有出现这样一种情况我们的覆盖数可以减小:某两个楼层的高度相同,且二者之间没有出现比它们更矮的楼层
这样的话,覆盖前一个口曾的时候我们适当向右拓展就可以覆盖到后一个和它高度相同的楼层了。
而对于其他的任何情况,两者高度不同,则一定需要两张不同的海报覆盖才可将其完全覆盖
而如何维护覆盖数可能减小的情况呢?我们可以采用单调栈的方式,这样如果两个相同高度的楼层中间有比它们更矮的楼层时,在更新到这一矮楼层时,我们已经将前者出栈了,再到后者更新时,就不会在栈中找到与其高度相同的楼层了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 250010
using namespace std;
int h[maxn],w[maxn];
int s[maxn];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i]>>h[i];
}
int top=0;
int ans=n;
for(int i=1;i<=n;i++)
{
if(h[i]>h[s[top]]) s[++top]=i;
else
{
while(h[i]<=h[s[top]])
{
if(h[i]==h[s[top]]) ans--;
top--;
}
s[++top]=i;
}
}
printf("%d\n",ans);
return 0;
}
\((2).\)合并果子
https://www.luogu.com.cn/problem/P1090
我们将所有的果子数量按从小到大来排列,显然果子数量少的合并次数越多是更优的解法,我们模拟一下即可
每合并一次就要重新排一次序,选出其中果子数量最小的两堆,若我们每次都用\(sort\)的话其复杂度为\(O(nlogn)\),最后会是\(O(n\ ^2logn)\)的复杂度,不符合题目的时间复杂度要求,我们可以用一个\(priority\_queue\)优先队列来维护即可(注意要将其重定义为小根堆)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define maxn 10010
using namespace std;
int a[maxn];
int main()
{
int n;
cin>>n;
priority_queue<int,vector<int>,greater<int> > q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
q.push(a[i]);
}
int ans=0;
for(int i=1;i<n;i++)
{
int m1=q.top();
q.pop();
int m2=q.top();
q.pop();
ans+=(m1+m2);
q.push(m1+m2);
}
printf("%d\n",ans);
return 0;
}
合并果子加强版
https://www.luogu.com.cn/problem/P6033
由于数据增加到了\(1e7\)的范围,原先\(O(nlogn)\)的算法就不再适用了,应该将原本的思想优化为一个\(O(n)\)复杂度的算法
然后注意到这里有个非常关键的地方是每堆果子的数量是\(1e5\)范围的,我们可以想到桶排的方法
\(2.\)差分和前缀和
海底高铁
https://www.luogu.com.cn/problem/P3406
很明显的做法就是找出每段路会在旅行中经过多少次,然后比较是采用买车票的方法还是买\(IC\)卡
没经过一段区间\([l,r]\),\(l\)路与\(l-1\)路经过次数的差就要加一,\(r\)路与\(r+1\)路经过次数的差也要加一,这也即是差分的做法
采用差分和前缀和的方法维护每段区间即可
不过,请注意数据范围,最后的答案应开\(long\) \(long\)型
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 100010
#define LL long long
using namespace std;
LL a[maxn],b[maxn],c[maxn];
LL w[maxn];
LL num[maxn];
LL sum[maxn];
LL p[maxn];
LL ans;
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>p[i];
for(int i=1;i<n;i++) cin>>a[i]>>b[i]>>c[i];
for(int i=1;i<m;i++)
{
int A=p[i],B=p[i+1];
if(A>B) swap(A,B);
sum[A]++;
sum[B]--;
}
for(int i=1;i<n;i++) num[i]=sum[i]+num[i-1];
for(int i=1;i<n;i++)
{
ans+=min(num[i]*a[i],c[i]+num[i]*b[i]);
}
printf("%lld\n",ans);
return 0;
}