前言
noip毒瘤給我們講上午的知識
知識總結
題目
T1 【模板】單調棧
題目描述
題目描述:
給出項數為 n 的整數數列 a1…n,定義函式 f(i) 代表數列中第 i 個元素之後第一個大於 ai 的元素的下標,即 f(i)=min i<j<=n,aj>ai {j}。若不存在,則 f(i)=0。
試求出 f(1…n)。
輸入格式:
第一行一個正整數 n。
第二行 n 個正整數 a1…n。
輸出格式;
一行 n 個整數 f(1…n) 的值。
樣例輸入:
5
1 4 2 3 5
樣例輸出:
2 5 4 5 0
資料規模:
$n≤3*106$,$a_i≤1*109$。
思路解析
單調棧的簡單運用。
考慮維護一個單調遞減的棧。
程式碼實現
#include <bits/stdc++.h>
using namespace std;
const int N = 3e6 + 5;
int n, a[N], f[N];
stack<int> s;
signed main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = n; i >= 1; i--) {
while (!s.empty() && a[s.top()] <= a[i]) {
s.pop();
}
if (!s.empty()) {
f[i] = s.top();
}
s.push(i);
}
for (int i = 1; i <= n; i++) {
printf("%d ", f[i]);
}
return 0;
}
T2 動物園的等待
題目描述
https://www.luogu.com.cn/problem/P1823
思路解析
只考慮每個人與其前面的人產生的數對。
維護一個自下而上遞減的棧。
在彈出的過程中,記錄答案即可。
程式碼實現
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct node {
int v, id;
};
stack<node> s;
int n, h, ans;
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &h);
node now = {h, 1};
for (; !s.empty() && s.top().v <= h; s.pop()) {
ans += s.top().id;
if (s.top().v == h)
now.id += s.top().id;
}
if (!s.empty()) {
ans++;
}
s.push(now);
}
printf("%lld\n", ans);
return 0;
}
T3 雨點
題目描述
https://www.luogu.com.cn/problem/P2698
思路解析
首先注意到答案一定是在端點上,否則出頭的部分就被浪費了。
按 x 排序後時間差等價於 y 的極差。
考慮二分答案後用單調佇列或者 ST 表維護極差。
不用二分答案也可以直接用雙指標+單調佇列去維護。
程式碼實現
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000;
const ll INF = 1e18;
ll n, D;
pair<ll, ll> a[maxn + 50];
#define x first
#define y second
int q1[maxn + 50], q2[maxn + 50];
int h1 = 1, t1 = 0;
int h2 = 1, t2 = 0;
int main() {
ios_base::sync_with_stdio(false);
cin >> n >> D;
for (int i = 1; i <= n; i++)
cin >> a[i].x >> a[i].y;
sort(a + 1, a + n + 1);
ll ans = INF;
for (int i = 1, j = 1; i <= n; i++) {
for (; h1 <= t1 && q1[h1] < i;)
h1++;
for (; h2 <= t2 && q2[h2] < i;)
h2++;
bool flag = false;
for (; j <= n; j++) {
if (h1 > t1 || h2 > t2) {
q1[++t1] = j;
q2[++t2] = j;
continue;
}
if (a[q1[h1]].y - a[q2[h2]].y >= D) {
flag = true;
break;
}
for (; h1 <= t1 && a[q1[t1]].y <= a[j].y;)
t1--;
for (; h2 <= t2 && a[q2[t2]].y >= a[j].y;)
t2--;
q1[++t1] = j;
q2[++t2] = j;
}
if (!flag)
break;
ans = min(ans, abs(a[i].x - a[j - 1].x));
}
if (ans == INF)
cout << -1 << "\n";
else
cout << ans << "\n";
return 0;
}
T4 jxcakak
題目描述
P2216 HAOI2007 理想的正方形 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)
思路解析
最大值和最小值是獨立的,可以單獨求解。
考慮用單調佇列去維護最大/小值,分行和列依次處理。
程式碼實現
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5, inf = 0x3f3f3f3f;
int a[N][N], w, h, n, ans = inf;
struct node {
int h, t, pos[N], val[N], flag;
void init(int f) {
memset(pos, 0, sizeof(pos));
memset(val, 0, sizeof(val));
h = 1;
t = 0;
flag = f;
return;
}
void pop_front(int lim) {
for (; h <= t && pos[h] <= lim;)
h++;
}
void push_back(int p, int v) {
for (; h <= t && (val[t] - v) * flag <= 0;)
t--;
t++;
pos[t] = p;
val[t] = v;
return;
}
int get_first() {
return val[h];
}
} colmx[N], colmn[N], rowmx, rowmn;
signed main() {
scanf("%d%d%d", &w, &h, &n);
for (int i = 1; i <= w; i++) {
for (int j = 1; j <= h; j++) {
scanf("%d", &a[i][j]);
}
}
for (int i = 1; i <= w; i++) {
colmx[i].init(1);
colmn[i].init(-1);
}
for (int j = 1; j <= h; j++) {
for (int i = 1; i <= w; i++) {
colmx[i].pop_front(j - n);
colmn[i].pop_front(j - n);
colmx[i].push_back(j, a[i][j]);
colmn[i].push_back(j, a[i][j]);
}
if (j >= n) {
rowmx.init(1);
rowmn.init(-1);
for (int i = 1; i <= w; i++) {
rowmx.pop_front(i - n);
rowmn.pop_front(i - n);
rowmx.push_back(i, colmx[i].get_first());
rowmn.push_back(i, colmn[i].get_first());
if (i >= n)
ans = min(ans, rowmx.get_first() + rowmn.get_first());
}
}
}
printf("%d", ans);
return 0;
}
T5 澤澤在英國
題目描述
澤澤用了100000000000000000000 mod 10天的時間爬出了長城。長城的另一端是一條隧道,澤澤走了進去……
澤澤不小心又到了英國。英國多雨,基本上隔2天就要下一場雨。澤澤人品不好,到這裡的時候天正在下酸雨。
酸雨會腐蝕建築物,讓那些建築物顯得很難看。英國有家工廠免費為一條街道的建築物的牆面塗油漆。心腸雖好,但是由於技術問題,他們只能塗出一個矩形。現在由於酸雨事態嚴重,街道辦主任下命令塗出面積最大的矩形。
街道上的建築物高度參差不齊,那該怎麼辦呢?
他們想到了澤澤。
澤澤接到了這個任務,就去測量了這個街道上的所有建築物的高度。
請根據澤澤的資料,計算出最大面積。
思路解析
答案矩形的高度一定是某個建築的高度。
用單調棧求出左右兩側能夠到達的寬度即可。
程式碼實現
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, a[N], h, l, ans;
signed main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
h = max(h, a[i]);
}
for (int i = 1; i <= h; i++) {
l = 0;
for (int j = 1; j <= n; j++) {
if (a[j] >= i) {
l++;
} else {
l = 0;
}
ans = max(i * l, ans);
}
}
printf("%d", ans);
return 0;
}