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
解题思路
1、单调栈
2、如果确定了一个矩形的左端点为l,右端点为r,那么矩形的高怎么确定?
• 根据题意,高度不能超过l到r中的最小值
• 要是整个矩形最大,那么高度就确定为l到r中的最小值
• 同理,如果确定了矩形的高度,那么左端点一定是越靠左,右端点越靠右,这个矩形的面积才可能最大
• 左端点可以确定为往左数第一个小于此高度的点
• 右端点可以确定为往右数第一个小于此高度的点
• 两遍单调栈处理出以每个点为高时的左右端点
3、单调递减栈,从左到右遍历,寻找右端点。
栈中存储的是各个矩形的编号。如果栈顶元素为空或者栈顶元素所表示的矩形的高小于入栈元素所表示的矩形的高,则入栈,如多不满足条件,则要将栈顶的元素弹出。
一旦发现第i个元素所表示的矩形的高比栈顶元素所表示的矩形的高小,则说明找到了第一个比栈顶元素所表示的高小的矩形所在编号,即找到了以栈顶元素所表示的小矩形为高的右端点。
同理用单调递减栈从右往左遍历找到左端点,最后算出面积。
4、时间复杂度O(n)
代码
#include<iostream>
#include<stack>
using namespace std;
int main()
{
int n;
while (cin >> n, n != 0)
{
long long* a = new long long[n];
long long* r = new long long[n];//存储矩形的右端点
long long* l = new long long[n];//存储矩形的左端点
for (int i = 0; i < n; i++)
cin >> a[i];
stack<int> s1;
stack<int> s2;
long long ans = 0;
long long max = 0;
for (int i = 0; i < n; i++)
{
while (s1.size() > 0 && a[s1.top()] > a[i])
{
r[s1.top()] = i - 1;
//cout << r[s1.top()] << " " << s1.top() << endl;
s1.pop();
}
s1.push(i);
}
while (s1.size() > 0)
{
r[s1.top()] = n-1;
s1.pop();
}
for (int i = n-1; i >=0; i--)
{
while (s2.size() > 0 && a[s2.top()] > a[i])
{
l[s2.top()] = i+1;
s2.pop();
}
s2.push(i);
}
while (s2.size() > 0)
{
l[s2.top()] = 0;
s2.pop();
}
for (int i = 0; i < n; i++)
{
ans = a[i] * (r[i] - l[i] + 1);
max = max > ans ? max : ans;
}
cout << max << endl;
delete[]a;
delete[]r;
delete[]l;
}
}
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 nn cities from the world map, and a[i]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 cc. And finally, it is required to give the asset value of each city after qq operations.
Could you help TT find the answer?
Input
The first line contains two integers n,q (1≤n,q≤2⋅105)(1≤n,q≤2·105) --the number of cities and operations.
The second line contains elements of the sequence aa: integer numbers a1,a2,…,an (−106≤ai≤106)(−106≤ai≤106).
Then qq lines follow, each line represents an operation. The ii-th line contains three integers l,rl,r and cc (1≤l≤r≤n,−105≤c≤105)(1≤l≤r≤n,−105≤c≤105) for the i-th operation.
Output
Print n integers a1,a2,…,ana1,a2,…,an one per line, and aiai should be equal to the final asset value of the ii-th city.
Output
Print n integers a1,a2,…,an one per line, and ai should be equal to the final asset value of the ii-th city.
解题思路
-
差分法
-
差分构造方式
原数组A,差分数组B,数组范围[1,n]
B[1]=A[1]
B[i]=A[i]-A[i-1] -
差分特点
B 数组前缀和 ⇔ A 数组元素值
SUM{B[1~i]} = A[i]
A 数组的区间加 ⇔ B 数组的单点修改
A[L]~A[R] 均加上 c 等价于 B[L] += c, B[R+1] -= c -
将原数组转变成差分数组,将区间修改转变为单点修改
-
A[L,R]+=c<=>B[L]+=c,B[R+1]-=c
-
B数组前缀和即为A数组最终数值
-
时间复杂度o(n+q)
代码
#include<iostream>
using namespace std;
int main()
{
int n, q;
cin >> n >> q;
long long* a = new long long[n+1];
long long* b = new long long[n+1];
for (int i = 1; i <= n; i++)
cin >> a[i];
b[1] = a[1];
for (int i = 2; i <= n; i++)
b[i] = a[i] - a[i - 1];
for (int i = 0; i < q; i++)
{
int l, k,c;
cin >> l >> k >> c;
b[l] += c;
if(k<n)
b[k + 1] -= c;
}
long long sum=0;
for (int i = 1; i <= n; i++)
{
sum += b[i];
cout << sum << " ";
}
delete[]a;
delete[]b;
}
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
解题思路
尺取法
- 又称双指针法,即在遍历数组的过程种,用两个相同方向进行扫描。
什么情况下能使用尺取法
- 所求解答案为一个连续区间
- 区间左右端点有明确移动方向。
是否可以用尺取法?
- 所求解为一个连续区间
- 区间左右端点移动有明确方向
当前[L,R]满足要求,则L++
当前[L,R]不满足要求,则R++
因此此题可以用尺取法
怎么判断当前[L,R]是否满足要求?
-
用Q,W,E,R分别记录下不包含区间[L,R]这一字段时,字符’Q’,‘W’,‘E’,'R’的个数
-
先通过替换使4类字符数量一致,再判断剩余空闲位置是否是4的倍数
-
MAX = max(sum1, sum2, sum3, sum4)
-
TOTAL = R – L + 1 • FREE = TOTAL -[(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)]
-
若 FREE ≥ 0 且为 4 的倍数,则满足要求;否则不满足
代码
#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
int main()
{
string s;
cin >> s;
int Q=0, W=0, E=0, R=0;//记录Q,W,E,R的数量
for (int i = 0; i < s.length(); i++)
{
if (s[i] == 'Q')
Q++;
else if (s[i] == 'W')
W++;
else if (s[i] == 'E')
E++;
else
R++;
}
int ans=s.length();
if (Q == W == E == R)
{
ans = 0;
cout << ans << endl;
return 0;
}
int l = 0, r = 0;
for (int i = l; i <= r; i++)
{
if (s[i] == 'Q')
Q--;
else if (s[i] == 'W')
W--;
else if (s[i] == 'E')
E--;
else
R--;
}
while (l < s.length() && r < s.length())
{
int mmax = max(max(Q, W), max(E, R));
int len = r - l + 1;
len -= (mmax - Q) + (mmax - W) + (mmax - E) + (mmax - R);
if (len >= 0 && len % 4 == 0)
{
ans = min(ans, r - l + 1);
if (s[l] == 'Q')
Q++;
else if (s[l] == 'W')
W++;
else if (s[l] == 'E')
E++;
else
R++;
l++;
}
else
{
r++;
if (s[r] == 'Q')
Q--;
else if (s[r] == 'W')
W--;
else if (s[r] == 'E')
E--;
else
R--;
}
}
cout << ans << endl;
return 0;
}
D - 滑动窗口
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
解题思路
- 单调队列
- 求窗口内最小值使用单调递增队列,每次返回队首元素
- 求窗口内最大值使用单调递减队列,每次返回队首元素
- 从左向右移动,每次半段下一个元素。在移动过程中需要判断队首元素是否在窗口内。若q.back()-q.front()+1<k则说明在。否则不在,弹出队首元素。
代码
#include<iostream>
#include<deque>
#include<stdio.h>
using namespace std;
int main()
{
int n, k;
scanf("%d %d", &n, &k);
int* a = new int[n];
int* mmin = new int[n];
int* mmax = new int[n];
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
deque<int> q1, q2;
int m = 0, j = 0;
for (int i = 0; i < k; i++)
{
while (!q1.empty() && a[q1.back()] > a[i])
q1.pop_back();
q1.push_back(i);
}
mmin[m++] = a[q1.front()];
for (int i = k; i < n; i++)
{
while (!q1.empty() && a[q1.back()] > a[i])
q1.pop_back();
q1.push_back(i);
//cout<<q1.front()<<endl;
while (!q1.empty() && q1.back() - q1.front() + 1 > k)
q1.pop_front();
mmin[m++] = a[q1.front()];
}
for (int i = k; i < n; i++)
{
while (!q1.empty() && a[q1.back()] > a[i])
q1.pop_back();
q1.push_back(i);
//cout<<q1.front()<<endl;
while (!q1.empty() && q1.back() - q1.front() + 1 > k)
q1.pop_front();
mmin[m++] = a[q1.front()];
}
for (int i = 0; i < k; i++)
{
while (!q2.empty() && a[q2.back()] < a[i])
q2.pop_back();
q2.push_back(i);
}
mmax[j++] = a[q2.front()];
for (int i = k; i < n; i++)
{
while (!q2.empty() && a[q2.back()] < a[i])
q2.pop_back();
q2.push_back(i);
while (!q2.empty() && q2.back() - q2.front() + 1 > k)
q2.pop_front();
mmax[j++] = a[q2.front()];
}
for (int i = 0; i < n - k; i++)
printf("%d ", mmin[i]);
printf("%d\n", mmin[n - k]);
for (int i = 0; i < n - k; i++)
printf("%d ", mmax[i]);
printf("%d\n", mmax[n - k]);
delete[]a;
delete[]mmin;
delete[]mmax;