Codeforces Round #333 (Div. 2)
B. Approximating a Constant Range
題意
對於一個長爲n
的序列且相臨的兩個數字相差1
,求一個最長的區間,區間的最大值和最小值的差爲1
。
思路
考慮dp,序列有三個狀態,
0. 序列的最後一個數字是該序列的最小值
1. 序列是一個全部數字一樣的序列
2. 序列的最後一個數字是該序列的最大值
於是
對於a[i] == a[i-1]
三種狀態都轉移到自己狀態即:
dp[i][0] = dp[i-1][0] + 1;
dp[i][1] = dp[i-1][1] + 1;
dp[i][2] = dp[i-1][2] + 1;
對於a[i] == a[i-1] + 1
只有0狀態和1狀態能夠轉移到2狀態, 其餘狀態的值爲1
dp[i][0] = 1;
dp[i][1] = 1;
dp[i][2] = max(dp[i-1][0], dp[i-1][1]) + 1;
同理對於a[i] == a[i-1] - 1
只有1狀態和2狀態能夠轉移到0狀態, 其餘狀態的值爲1
dp[i][0] = max(dp[i-1][1], dp[i-1][2]) + 1;
dp[i][1] = 1;
dp[i][2] = 1;
於是在轉移時找個最大值就是了。
code
#include <bits/stdc++.h>
using namespace std;
int n;
int a[100000 + 5];
int dp[100000 + 5][3];
int main () {
scanf ("%d", &n);
for (int i=0; i<n; i++) {
scanf ("%d", &a[i]);
}
dp[0][0] = 1;
dp[0][1] = 1;
dp[0][2] = 1;
int ans = 1;
for (int i=1; i<n; i++) {
if (a[i] == a[i-1]) {
dp[i][0] = dp[i-1][0] + 1;
dp[i][1] = dp[i-1][1] + 1;
dp[i][2] = dp[i-1][2] + 1;
} else if (a[i] == a[i-1] + 1) {
dp[i][0] = 1;
dp[i][1] = 1;
dp[i][2] = max(dp[i-1][0], dp[i-1][1]) + 1;
} else if (a[i] == a[i-1] - 1) {
dp[i][0] = max(dp[i-1][1], dp[i-1][2]) + 1;
dp[i][1] = 1;
dp[i][2] = 1;
}
for (int j=0; j<3; j++) {
ans = max(ans, dp[i][j]);
}
}
printf ("%d\n", ans);
return 0;
}
C. The Two Routes
題意
n
個城市構成了一幅完全圖,圖上的邊只能通過火車或者只能通過汽車。現在有一輛火車和一輛汽車從城市1出發,前往城市n,每小時經過一條邊,途中不能同時到達同一個城市。問最少需要經過多少小時才能到達n城市。
思路
學霸說這是完全圖,那麼一定有一條1->n
的邊。那麼肯定會走這條邊(要麼火車要麼汽車,可以知道)。那麼剩下的那個就bfs1就行了。
code
using namespace std;
int n, m;
int _m[405][405];
int h[405];
int bfs(int a) {
memset(h, -1, sizeof(h));
queue<int> q;
q.push(1);
h[1] = 0;
while (!q.empty()) {
int now = q.front(); q.pop();
if (now == n) return h[now];
for (int i=1; i<=n; i++) {
if (h[i] == -1 && _m[now][i] == a) {
q.push(i);
h[i] = h[now] + 1;
}
}
}
return -1;
}
int main() {
scanf ("%d%d", &n, &m);
memset(_m, 0, sizeof(_m));
int u, v;
for (int i=0; i<m; i++) {
scanf ("%d%d", &u, &v);
_m[u][v] = 1;
_m[v][u] = 1;
}
int ans;
if (_m[1][n] == 1) {
ans = bfs(0);
} else {
ans = bfs(1);
}
printf ("%d\n", ans);
return 0;
}
D. Lipshitz Sequence
題意
對於序列S,定義L(s)爲
現在給出個原始序列,q次查詢,查詢區間[l, r]的所有字串si的L(si)的和。
題意
這種題目絕對不是剛正面的,仔細想想,肯定有很多字串的L值是一樣的。我們可以去計算這個值被多少個字串用。
首先我們可以想到,L(s)求出來的i,j一定是連續的。那麼我們可以預處理連續的差(其實也可以不處理)的序列T。對於這個序列T區間_l
到_r
的最大值是maxT
,那麼_l
到_r
中的包含maxT
的子串Ti的L(Ti)的值一定是maxT
。而我們可以很方便的用單調棧處理出查詢區間的所有的差的_l
和_r
。
code
#include <bits/stdc++.h>
using namespace std;
int n, q;
int a[100000 + 5];
int _l[100000 + 5];
int _r[100000 + 5];
int stac[100000 + 5], cstac;
long long ans;
void solve (int l, int r) {
int cstac = 0;
for (int i=l; i<r; i++) {
int s = fabs(a[i+1] - a[i]);
while (cstac > 0 && fabs(a[stac[cstac]+1] - a[stac[cstac]]) < s) {
_r[stac[cstac]] = i - 1;
cstac--;
}
if (cstac == 0) {
_l[i] = l;
} else if (cstac > 0){
_l[i] = stac[cstac] + 1;
}
stac[++cstac] = i;
}
while (cstac > 0) {
_r[stac[cstac]] = r - 1;
cstac --;
}
ans = 0L;
for (int i=l; i<r; i++) {
ans += 1LL * fabs(a[i+1] - a[i]) * (i - _l[i] + 1) * (_r[i] - i + 1);
}
}
int main () {
scanf ("%d %d", &n, &q);
for (int i=1; i<=n; i++) {
scanf ("%d", &a[i]);
}
solve(1, n);
int l, r;
for (int i=0; i<q; i++) {
scanf ("%d %d", &l, &r);
solve (l, r);
printf ("%lld\n", ans);
}
return 0;
}