A-最大矩形
题目
给一个直方图,求直方图中的最大矩形的面积。
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
做法
单调栈利用数组s实现单调递增栈,每次从数组h(矩形的高)中取出一点,如果比栈中元素大,就可以入栈,否则,将栈中比h【i】大的元素都取出,同时计算取出的点可扩展的单位长度,计算矩形的面积。
计算横向单位长度用到数组r,表示栈里的数可以向左延伸的单位长度。举个例子,要出栈的值有a,b(b先出栈),则a可以扩展的长度就等于a向左延伸的长度+b矩形的单位长度。因为要进站的点一定是小于a的。而要进栈的点可以向左延伸的长度就等于出栈的最后一个数能延伸的单位长度+1。
代码
#include<iostream>
#include<string.h>
using namespace std;
int h[100009];//矩形的高
int r[100009];//记录左边可以扩展的单位长度
int s[100009];//单调栈
int main()
{
int n;
while(cin>>n)
{
if(n==0)
break;
for(int i=0;i<n;i++)
cin>>h[i];
h[n]=0;
memset(r,0,100009);
memset(s,0,100009);
int j=0;//跟踪栈的下标
long long result=0;//最大矩形面积
for(int i=0;i<=n;i++)
{
if(h[i]>s[j])
{
j++;
s[j]=h[i];
r[j]=1;
}
else
{
int l=0;//矩形可的扩展单位长度
while(h[i]<s[j]&&j>=1)
{
l=l+r[j];
long long z=(long long)l*(long long)s[j];
if(result<z)
result=z;
j--;
}
s[++j]=h[i];//大数出栈后,小数可以入栈
r[j]=l+1;//小数可以向左扩展的单位长度
}
}
cout<<result<<endl;
}
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
做法
前缀和与差分
差分构造方式:
原数组a,差分数组b,数组范围【1,n】,则b[1]=b[1],b[i]=a[i]-a[i-1]。
则sum{b[1~i]}=a[i]。若a[l]到a[r]之间的数均加上c,就等价于b[l]+=c,b[r+1]-=c。
这样就可以通过修改数组b的值,给数组a区间统一加上或减去一个数。修改后,利用a[i]=b[i]+a[i-1]输出数组a。
代码
#include<iostream>
using namespace std;
long long a[10000000];
long long b[10000000];
int main()
{
long long n,q;
cin>>n>>q;
cin>>a[1];
b[1]=a[1];
for(int i=2;i<=n;i++)
{
cin>>a[i];
b[i]=a[i]-a[i-1];
}
for(int i=0;i<q;i++)
{
long long l,r,t;
cin>>l>>r>>t;
b[l]+=t;
b[r+1]-=t;
}
a[1]=b[1];
cout<<b[1]<<' ';
for(int i=2;i<=n;i++)
{
a[i]=b[i]+a[i-1];
cout<<a[i]<<' ';
}
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]满足要求,则l++;当前[l,r]不满足要求,则r++。
给定[l,r],判断是否满足要求:
用sum1,sum2,sum3,sum4记录不包含区间[l,r]这一段时,字符QWER的个数。
先通过替换使四类字符数量一致,再判断剩余空闲位置是否为4的倍数。
MAX=max(sum1,sum2,sum3,sum4)
total=r-l+1
free=total-[(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)]
若free>=0且为4的倍数,则满足要求,否则不满足。
我的做法:起初考虑一次读入一行,再利用strlen求字符串长度,然后每次选中区间,直接计算区间外字符各自的数量。发现超时,可能是strlen的复杂度,以及计算区间外字符数量涉及数据比较庞大的问题。所以改为按字符读入,同时计算字符各自的数量。之后求区间外字符数量时可以计算区间内字符串数量,相减得到区间外字符数量。
代码
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
char c[1000000];
int main()
{
cin>>c;
int n=0;
//scanf("%[^\n]",&c);
//cin.getline(c,1000000);
//int n=strlen(c);
int s1=0,s2=0,s3=0,s4=0;
for(int i=0;c[i]!='\0';i++)
{
if(c[i]=='Q')
s1++;
else if(c[i]=='W')
s2++;
else if(c[i]=='E')
s3++;
else if(c[i]=='R')
s4++;
n++;
}
if(s1==s2&&s2==s3&&s3==s4)
cout<<0<<endl;
else
{
int l=1,r=1;
int total=0;
int t=n;
while(l<=r&&r<=n)
{
int sum1=s1,sum2=s2,sum3=s3,sum4=s4;
for(int i=l;i<=r;i++)
{
if(c[i]=='Q')
sum1--;
else if(c[i]=='W')
sum2--;
else if(c[i]=='E')
sum3--;
else if(c[i]=='R')
sum4--;
}
int Max=max(max(sum1,sum2),max(sum3,sum4));
total=r-l+1;
int free=total-((Max-sum1)+(Max-sum2)+(Max-sum3)+(Max-sum4));
if(free>=0&&free%4==0)
{
l++;
if(t>total)
t=total;
}
else
r++;
}
cout<<t<<endl;
}
return 0;
}
D - 滑动窗口
题目
ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
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<iostream>
#include<stdio.h>
using namespace std;
int a[1000100];//数列
int q[1000100];//队列
int Max[1000100];//滑动窗口最大值
int Min[1000100];//滑动窗口最小值
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int l=1,r=0;
for(int i=1;i<=n;i++)
{//q[i]队列中i元素的下标
while(r>=l&&a[i]<a[q[r]])
r--;//弹出队尾
q[++r]=i;//把a[i]加入队列
if(q[r]-q[l]+1>k)
l++;//维护队列大小
if(i-k+1<1)
continue;
else
Min[i-k+1]=a[q[l]];
}
l=1,r=0;
for(int i=1;i<=n;i++)
{
while(r>=l&&a[i]>a[q[r]])
r--;
q[++r]=i;
if(q[r]-q[l]+1>k)
l++;
if(i-k+1<1)
continue;
else
Max[i-k+1]=a[q[l]];
}
for(int i=1;i<=n-k+1;i++)
cout<<Min[i]<<' ';
cout<<endl;
for(int i=1;i<=n-k+1;i++)
cout<<Max[i]<<' ';
return 0;
}