A.最大矩形
给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。
Input
输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。
Output
对于每组测试数据输出一行一个整数表示答案。
Sample Input
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
Sample Output
8
4000
解题思路
我们枚举直方图中每一个矩形为要求的最大阴影面积的左边界或者右边界。当其为左边界时,需要找到右边第一个高度小于他的矩形,从而能使阴影面积最大,当其为右边界时,则需要找到左边第一个高度小于他的矩形。枚举每一个矩形记录下阴影面积最大的矩形,即为答案。
解题方法
使用单调递增栈来寻找一个矩形右边第一个高度小于他的矩形。当栈中进入一个新的数使得栈顶元素被弹出,被弹出的所有元素,其右边第一个高度小于他的矩形即为刚才进入栈中的数。同理,使用单调递增栈反向遍历一遍可以找到左边第一个高度小于他的矩形。
代码实现
#include<cstdio>
#include<iostream>
using namespace std;
long long a[100010];
long long st[100010];
long long R[100010];
long long L[100010];
int main()
{
int n=0;
while(cin>>n)
{
if(n==0) break;
int t=-1;
long long maxx=0;
for(int i=0;i<n;i++) scanf("%lld",&a[i]);
for(int i=0;i<n;i++)
{
while(t>=0&&a[i]<a[st[t]])
{
R[st[t]]=i;
t--;
}
t++;
st[t]=i;
}
while(t>=0)
{
R[st[t]]=n;
t--;
}
t=-1;
for(int i=n-1;i>=0;i--)
{
while(t>=0&&a[i]<a[st[t]])
{
L[st[t]]=i;
t--;
}
t++;
st[t]=i;
}
while(t>=0)
{
L[st[t]]=-1;
t--;
}
for (int i=0;i<n;i++)
{
if(maxx<a[i]*(R[i]-L[i]-1)) maxx=a[i]*(R[i]-L[i]-1);
}
printf("%lld\n",maxx);
}
return 0;
}
B.TT’s Magic Cat
Thanks to everyone’s help last week, TT finally got a cute cat. But what TT didn’t expect is that this is a magic cat.
One day, the magic cat decided to investigate TT’s ability by giving a problem to him. That is select n cities from the world map, and a[i] represents the asset value owned by the i-th city.
Then the magic cat will perform several operations. Each turn is to choose the city in the interval [l,r] and increase their asset value by c. And finally, it is required to give the asset value of each city after q operations.
Could you help TT find the answer?
Input
The first line contains two integers n,q (1≤n,q≤2⋅105) — the number of cities and operations.
The second line contains elements of the sequence a: integer numbers a1,a2,…,an (−106≤ai≤106).
Then q lines follow, each line represents an operation. The i-th line contains three integers l,r and c (1≤l≤r≤n,−105≤c≤105) for the i-th operation.
Output
Print n integers a1,a2,…,an one per line, and ai should be equal to the final asset value of the i-th city.
Examples
Input
4 2
-3 6 8 4
4 4 -2
3 3 1
Output
-3 6 9 2
Input
2 1
5 -2
1 2 4
Output
9 2
Input
1 2
0
1 1 -8
1 1 -6
Output
-14
解题思路
题目大意是要把某一个区间的数都加上同一个数,如果暴力的求解,遍历整个区间,由于操作次数很多,显然时间撑不住。这里我们使用差分数组可以很方便的完成题目。
解题方法
差分数组是将原数组处理,处理方法是:当i=0时,b[0]=a[0],当i>0时,b[i]=a[i]-a[i-1]。此时要求原数组方法为将差分数组求和,a[i]=b[0]+b[1]±-----b[i]。如果我们把原数组某一个区间加上一个数,只需要把差分数组左区间的数加上这个数,右区间右边的数减去这个数即可。比如原数组[4,16]加上5。
只需要把b[4]=b[4]+5,b[17]=b[17]-5即可。最终只需要遍历一遍,将差分数组求和就可以求出经过所有操作后的数组。
代码实现
#include<cstdio>
#include<iostream>
using namespace std;
long long a[200200];
long long b[200200];
int main()
{
int n,q,l,r;
long long c;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) b[i]=a[i]-a[i-1];
for(int i=1;i<=q;i++)
{
scanf("%d%d%lld",&l,&r,&c);
b[l]+=c;
b[r+1]-=c;
}
printf("%lld",b[1]);
long long sum=b[1];
for(int i=2;i<=n;i++)
{
sum+=b[i];
printf(" %lld",sum);
}
printf("\n");
return 0;
}
C.平衡字符串
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
Input
一行字符表示给定的字符串s
Output
一个整数表示答案
Examples
Input
QWER
Output
0
Input
QQWE
Output
1
Input
QQQW
Output
2
Input
QQQQ
Output
3
Note
1<=n<=10^5
n是4的倍数
字符串中仅包含字符 ‘Q’, ‘W’, ‘E’ 和 ‘R’.
解题思路
首先题意要求可以替换一段连续的区间,由于区间是连续的,可以发现当一段区间不能替换时,将区间缩小也是不能替换的,因此,我们倾向于将区间扩大,这就可以用到尺取法。
解题方法
使用尺取法选取一段区间,判断这段区间是否能被替换从而满足题目要求。如果能被替换,此时将区间扩大不能获得更优解,因此我们将左边界右移,缩小区间。当区间不能被替换,则需要扩大区间去满足题目要求,此时将右区间右移,扩大区间。判断区间是否能被替换也很简单,记录区间外四种字符的数量,按数量最大的字符为标准,检查区间大小是否能使区间外其他字符增加到同一数量,检查区间大小时要注意,若区间较大需要考虑模4(即每个字符同时加一)。
代码实现
#include<cstdio>
#include<iostream>
using namespace std;
char s[100000];
int s1,s2,s3,s4;
int ans=100000;
int main()
{
cin>>s;
s1=0,s2=0,s3=0,s4=0;
int tot=0;
while(s[tot]!='\0')
{
if(s[tot]=='Q') s1++;
else if(s[tot]=='W') s2++;
else if(s[tot]=='E') s3++;
else if(s[tot]=='R') s4++;
tot++;
}
if(s1==s2&&s2==s3&&s3==s4)
{
cout<<0<<endl;
return 0;
}
int l=0,r=0;
while(r<=tot-1)
{
int s11=0,s22=0,s33=0,s44=0;
for(int i=l;i<=r;i++)
{
if(s[i]=='Q') s11++;
else if(s[i]=='W') s22++;
else if(s[i]=='E') s33++;
else if(s[i]=='R') s44++;
}
s11=s1-s11;
s22=s2-s22;
s33=s3-s33;
s44=s4-s44;
int maxx=0;
if(s11>maxx) maxx=s11;
if(s22>maxx) maxx=s22;
if(s33>maxx) maxx=s33;
if(s44>maxx) maxx=s44;
int len=r-l+1;
int now=len-((maxx-s11)+(maxx-s22)+(maxx-s33)+(maxx-s44));
if(now>=0&&now%4==0)
{
if(len<ans) ans=len;
if(l==r) l++,r++;
else l++;
}
else r++;
}
cout<<ans<<endl;
}
D.滑动窗口
ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
Window position | Minimum value | Maximum value |
---|---|---|
[1 3 -1] -3 5 3 6 7 | -1 | 3 |
1 [3 -1 -3] 5 3 6 7 | -3 | 3 |
1 3 [-1 -3 5] 3 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | -3 | 5 |
1 3 -1 -3 [5 3 6] 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7] | 3 | 7 |
Input
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
Output
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
Sample Input
8 3
1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7
解题思路
如题目名所说这是一个使用滑动窗口解决的题目。当然我们不可能模拟一个窗口在上面滑动,这样太麻烦而且效率太低。我们可以使用单调队列来完成滑动窗口。
解题方法
我们使用单调递减队列来找滑动窗口里的最大值,使用单调递增队列寻找滑动窗口里的最小值。当寻找最大值时,队首元素一定为最大值,寻找最小值同理。一个单调递减队列,当一个新元素入队,如果新元素比队尾元素大,将队尾元素弹出直到队尾元素大于将要入队的元素。判断队首元素出队只需要记录一下这个元素在数组中的下标,判断他是否还在滑动窗口中,若不在即可出队。
代码实现
#include<cstdio>
#include<iostream>
using namespace std;
int a[10001000];
int q[10001000];
int main()
{
int n,k,l,r;
cin>>n>>k;
for(int i=0;i<n;i++) scanf("%d",&a[i]);
l=0,r=-1;
for(int i=0;i<n;i++)
{
if(l<=r&&q[l]<i-k+1) l++;
while(l<=r&&a[q[r]]>=a[i]) r--;
q[++r]=i;
if(i>=k-1) printf("%d ",a[q[l]]);
}
cout<<endl;
l=0,r=-1;
for(int i = 0;i<n;i++)
{
if(l<=r&&q[l]<i-k+1) l++;
while(l<=r&&a[q[r]]<=a[i]) r--;
q[++r]=i;
if(i>=k-1) printf("%d ",a[q[l]]);
}
cout<<endl;
return 0;
}