Codeforces Round #333 (Div. 2) B C D

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)爲

max(|s[i]s[j]||ij|)(ij)

現在給出個原始序列,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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值