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<iostream>
using namespace std;
long long a[100001];
long long le[100001];
long long ri[100001];
int stack[100001];
int top = 0;
int n=-1;
int main(){
// freopen("in1.txt","r",stdin);
scanf("%d",&n);
while(n!=0){
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
ri[i] = 0;
}
a[n+1] = 0;
a[0] = 0;
int top = 0;
stack[0] = 1;
top = 0;
stack[0] = n;
for(int i=n-1;i>=0;i--){
if(a[i]>=a[stack[top]]){
stack[++top] = i;
}
else{
while(top>=0&&a[i]<a[stack[top]]){
le[stack[top--]] = i;
}
stack[++top] = i;
}
}
long long s = 0;
for(int i=1;i<=n;i++){
long long tmp = (ri[i]-le[i]-1)*a[i];
s = max(s,tmp);
}
cout<<s<<endl;
scanf("%d",&n);
}
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
思路
本题表面看起来很简单,只需要求出在给定区间内的所有ai进行更新即可,但是,如果采用暴力做法每一次操作都将区间内的值都更新一边则会超时,所以,应该采用差分与前缀和的方法,只更新单点,即可保证区间内的点更新同一值,最后输出差分数组的前缀和即可。
全部代码如下
#include<iostream>
using namespace std;
int n,q;
long long a[200001];
long long b[200001];
int main(){
// freopen("in2.txt","r",stdin);
cin>>n>>q;
scanf("%lld",&a[1]);
b[1] = a[1];
for(int i=2;i<=n;i++){
scanf("%lld",&a[i]);
b[i] = a[i] - a[i-1];
}
int l,r,c;
while(q){
q--;
scanf("%d%d%d",&l,&r,&c);
b[l] += c;
b[r+1] -= c;
}
long long ds = 0;
for(int i=1;i<=n;i++){
ds += b[i];
printf("%lld ",ds);
}
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'.
思路
替换子串[L,R],则对于非此区间的各个字符数目应当与最大的字符数目的差的和即为需要更换的字符串数目,即将较少的字符与最多的字符对齐。若对齐后剩下的字符数目>=0且为4的倍数,则说明这些字符可以均匀放置四个字符,则此时的[L,R]区间即满足条件。再通过此条件采用尺取法在字符串上移动,即可得解。
全部代码如下
#include<iostream>
using namespace std;
string s;
int n=0;
int sum1 = 0,sum2 = 0,sum3 = 0,sum4 = 0;
bool check(int l,int r){
int t1 = sum1,t2 = sum2,t3 = sum3,t4 = sum4;
for(int i=l;i<=r;i++){
if(s[i]=='Q') t1--;
else if(s[i]=='W') t2--;
else if(s[i]=='E') t3--;
else t4--;
}
int Max = max(max(max(t1,t2),t3),t4);
int total = r-l+1;
int free = total - ((Max-t1)+(Max-t2)+(Max-t3)+(Max-t4));
if((free>=0)&&(free%4==0)) return true;
return false;
}
int main(){
// freopen("in3.txt","r",stdin);
cin>>s;
n = s.length();
int ans = n;
int l=0,r=0;
for(int i=0;i<n;i++){
if(s[i]=='Q') sum1++;
else if(s[i]=='W') sum2++;
else if(s[i]=='E') sum3++;
else sum4++;
}
while(r<n&&l<n){
if(check(l,r)){
ans = min(ans,r-l+1);
l++;
if(l==r){
l++;
r++;
}
}
else r++;
}
cout<<ans<<endl;
return 0;
}
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
思路
题目要求找到每个窗口的最小值和最大值,也就是局部最值,所以可以利用单调队列解决这类问题。在入队的同时也要注意是否队首元素属于当前窗口,若不属于,则将其弹出。
注意
在输出时应该使用cout,使用printf会超时。
全部代码如下
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
/*
scanf 比 cin 快,
此题 cout 比 printf快。
*/
using namespace std;
int n,k;
int Min[1000000],Max[1000000];
int q[1000000];
int rq[1000000];
int label[1000000];
int rlabel[1000000];
int tmp;
int main(){
// freopen("in4.txt","r",stdin);
int front=0,back=-1;
int rfront=0,rback=-1;
cin>>n>>k;
int myindex = 0;
int rmyindex = 0;
scanf("%d",&q[0]);
rq[0] = q[0];
Min[0] = q[0];
Max[0] = q[0];
back ++;
rback ++;
label[0] = 0;
rlabel[0] = 0;
for(int i=1;i<n;i++){
myindex = label[front];
rmyindex = rlabel[rfront];
// cout<<"index1:"<<myindex<<" front:"<<front<<endl;
if((i-myindex)>=k){
front++;
}
if((i-rmyindex)>=k){
rfront++;
}
scanf("%d",&tmp);
if(tmp>=q[back]){
q[++back] = tmp;
label[back] = i;
}
else{
while(back>=front&&tmp<q[back]){
back--;
}
q[++back] = tmp;
label[back] = i;
}
if(tmp<=rq[back]){
rq[++rback] = tmp;
rlabel[rback] = i;
}
else{
while(rback>=rfront&&tmp>rq[rback]){
rback--;
}
rq[++rback] = tmp;
rlabel[rback] = i;
}
Min[i] = q[front];
Max[i] = rq[rfront];
}
for(int i=k-1;i<n-1;i++){
cout<<Min[i]<<" ";
}
cout<<Min[n-1];
printf("\n");
for(int i=k-1;i<n-1;i++){
cout<<Max[i]<<" ";
}
cout<<Max[n-1];
return 0;
}