2019UMS培训day3解题报告

T1:赌徒

链接:https://www.luogu.org/problem/T92080

$sol1:$设$f[i][j]$表示目前得到了$x$分,此时骰子面朝$i$,那么$f[i+k][k]=max(f[i][j]+1,f[i+k][k])$。

先预处理之后直接查询即可。

总复杂度$O(n)$。

$sol2:$考虑最小步数,则先去用$5,6$使之后次数更小,然后用其他面去凑,注意$7$的特判。

代码$(sol1)$:

#include <bits/stdc++.h>
const int MAXN = 1000001;
const int INF = 1 << 30;
using namespace std;
int f[MAXN][10], t, n;
void init() {
    memset(f, 127, sizeof(f));
    f[0][1] = 0;
    for(int i = 0; i <= MAXN; i++) {
        for(int j = 1; j <= 6; j++) {
            for(int k = 1; k <= 6; k++) {
                if(j != k && j + k != 7)
                    f[i + k][k] = min(f[i + k][k], f[i][j] + 1); 
            }
        }
    }
}
int dp(int x) {
    int ans = INF;
    for(int i = 1; i <= 6; i++)
        ans = min(ans, f[x][i]);
    return ans;
}
int main() {
    cin >> t;
    init();
    while(t--) {
        cin >> n;
        cout << dp(n) << endl;
    }
    return 0;
} 

T2:盒子

链接:https://www.luogu.org/problem/T92083

$sol:$设$A[l][r]$表示人处于先手,在$l,r$的范围内进行选择,得到的最优答案,反之$B[l][r]$表示人处于后手......

那么一个人得到的最大的分即可以表示为$f[l][r]=max(A[l+1][r]+a[l],B[l][r-1]+a[r])$。

对于$B[l][r]$,因为是后手,所以可以直接求出。

注意要使用记忆化搜索。

最后答案即为$A(1,n)-B(1,n)$。

代码:

#include <bits/stdc++.h>
const int MAXN = 1050;
using namespace std;
int t, n, a[MAXN], sum[MAXN], f[MAXN][MAXN];
int A(int l, int r);
int B(int l, int r);
int B(int l, int r) { return sum[r] - sum[l - 1] - A(l, r); }
int A(int l, int r) {
    if(l == r)
        return a[l];
    if(f[l][r])
        return f[l][r];
    f[l][r] = max(B(l + 1, r) + a[l], B(l, r - 1) + a[r]);
    return f[l][r];
}
void init() {
    memset(f, 0, sizeof(f));
}
int main() {
    cin >> t;
    for(int i = 1; i <= t; i++) {
        cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
            sum[i] = sum[i - 1] + a[i];
        }
        cout << A(1, n) - B(1, n) << endl;
        init();
    }
    return 0;
}

T3:楼房修建

链接:https://www.luogu.org/problem/P4198

$sol1:$这道题本质就是求区间内的最长斜率上升子序列,考虑用线段树维护。

线段树里维护该区间的斜率最大值,以及仅该区间的最长斜率上升子序列。

斜率的$pushup$很简单,对于后者,考虑到修改一个点,对其左侧的答案没有影响,那我们可以去递归其右子树。

对于右子树,把当前区间从中划分为两部分,若左边区间的斜率最大值都比当前传进来的斜率要小,那么只用去递归右子树。

否则答案即为递归左子树的答案加上$ans[p]-ans[ls]$。

做法很玄学。

$sol2:$分块(咕)

代码$(sol1)$:

#include <bits/stdc++.h>
#define ls p << 1
#define rs p << 1 | 1
const int MAXN = 100050;
using namespace std;
struct node {
    double Max;
}a[MAXN << 2];
int n, m, x, y, ans[MAXN << 2];
int read() {
    int x = 0;
    bool sign = false;
    char alpha = 0;
    while(!isdigit(alpha)) {
        sign |= alpha == '-';
        alpha = getchar();
    }
    while(isdigit(alpha)) {
        x = (x << 1) + (x << 3) + (alpha ^ 48);
        alpha = getchar();
    }
    return sign ? -x : x;
}
struct Segment_Tree {
    void push_up(int p) { a[p].Max = max(a[ls].Max, a[rs].Max); }
    int query(int p, int l, int r, double k) {
        if(a[p].Max <= k)
            return 0;
        if(l == r)
            return a[p].Max > k;
        int mid = (l + r) >> 1;
        if(a[ls].Max <= k)
            return query(rs, mid + 1, r, k);
        else 
            return query(ls, l, mid, k) + ans[p] - ans[ls];         
    }
    void update(int p, int l, int r, int x, double k) {
        if(l == r) {
            ans[p] = 1;
            a[p].Max = k;
            return ; 
        }
        int mid = (l + r) >> 1;
        if(x <= mid)
            update(ls, l, mid, x, k);
        if(x > mid) 
            update(rs, mid + 1, r, x, k);
        push_up(p);
        ans[p] = ans[ls] + query(rs, mid + 1, r, a[ls].Max);
    }
}Tree;
int main() {
    n = read();
    m = read();
    for(int i = 1, x, y;i <= m; i++) {
        x = read();
        y = read();
        Tree.update(1, 1, n, x, (double)y / x);
        cout << ans[1] << endl;
    }
    return 0;
}

T4:弹飞绵羊

$sol1:$分块

(咕)

posted @ 2019-08-06 20:33  BeyondLimits  阅读(183)  评论(0)    收藏  举报