AtCoder Educational DP Contest

AtCoder Educational DP Contest

AtCoder Educational DP Contest

[於 Feb, 2020 完成]

這套從簡到難的 DP 題組,有不少經典題,很適合照順序刷。 開始刷了之後,才發現自己實力的不足,有幾題想了很久還是想不出來,只好去翻別人的解答。刷完發現 code 都不長,狀態也不難列,但轉移式都要想很久。

A. Frog 1

 1#include<bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    int N; scanf("%d", &N);
 5    int dp[2] = {}, a[2] = {};
 6    scanf("%d%d", &a[1], &a[0]);
 7    dp[0] = abs(a[1] - a[0]);
 8    for (int i = 3; i <= N; i++) {
 9        int x; scanf("%d",&x);
10        dp[i % 2] = min(dp[i % 2] + abs(x - a[i % 2]),
11                    dp[(i + 1) % 2] + abs(x - a[(i + 1) % 2]));
12        a[i % 2] = x;
13    }
14    printf("%d\n", dp[N % 2]);
15    return 0;
16}

B. Frog 2

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 100005
 4int main() {
 5    int n, k;
 6    scanf("%d%d", &n, &k);
 7    int a[maxn], dp[maxn];
 8    for (int i = 1; i <= n; i++)
 9        scanf("%d", &a[i]);
10    for (int i = 1; i <= n; i++) {
11        if (i <= k) dp[i] = abs(a[i] - a[1]);
12        else {
13            dp[i] = dp[i - 1] + abs(a[i] - a[i - 1]);
14            for (int j = 2; j <= k; j++)
15                dp[i] = min(dp[i],
16                            dp[i - j] + abs(a[i] - a[i - j]));
17        }
18    }
19    printf("%d\n", dp[n]);
20    return 0;
21}

C. Vacation

 1#include<bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    int n; scanf("%d", &n);
 5    int dp[2][3] = {};
 6    for (int i = 1; i <= n; i++)
 7        for (int j = 0; j < 3; j++) {
 8            int x; scanf("%d",&x);
 9            dp[i % 2][j] = x + max(dp[(i + 1) % 2][(j + 1) % 3],
10                                   dp[(i + 1) % 2][(j + 2) % 3]);
11        }
12    printf("%d\n", max(dp[n % 2][0], 
13                   max(dp[n % 2][1], dp[n % 2][2])));
14    return 0;
15}

D. Knapsack 1

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxW 100005
 4int main() {
 5    int n, W; scanf("%d%d", &n, &W);
 6    long long dp[maxW] = {};
 7    for(int i = 0; i < n; i++) {
 8        int w, v; scanf("%d%d", &w, &v);
 9        for(int j = W; j >= w; j--)
10            dp[j] = max(dp[j], dp[j - w] + v);
11    }
12    printf("%lld\n", dp[W]);
13    return 0;
14}

E. Knapsack 2

dp[j] 為價值為 j 需要的物品最低重量。

 1#include<bits/stdc++.h>
 2using namespace std;
 3int main() {
 4    int n, W;
 5    scanf("%d%d", &n, &W);
 6    int dp[100005] = {};
 7    for (int i = 1; i < 100005; i++)
 8        dp[i] = 1000000005;
 9    for (int i = 0; i < n; i++) {
10        int v, w; scanf("%d%d", &w, &v);
11        for(int j = 100000; j >= v; j--)
12            dp[j] = min(dp[j], dp[j-v] + w);
13    }
14    for (int i = 100000; i >= 0; i--)
15        if (dp[i] <= W) {
16            printf("%d\n", i);
17            break;
18        }
19    return 0;
20}

F. LCS

 1#include<bits/stdc++.h>
 2using namespace std;
 3int dp[3005][3005];
 4int p[3005][3005];
 5int main() {
 6    ios_base::sync_with_stdio(0), cin.tie(0);
 7    string s,t; cin >> s >> t;
 8    int lens = s.length(), lent = t.length();
 9    for (int i = 1; i <= lens; i++) {
10        for (int j = 1; j <= lent; j++) {
11            if (s[i - 1] == t[j - 1]) {
12                dp[i][j] = dp[i - 1][j - 1] + 1;
13                p[i][j] = 1;
14            } else if (dp[i][j - 1] > dp[i - 1][j]) {
15                dp[i][j] = dp[i][j - 1];
16                p[i][j] = 2;
17            } else {
18                dp[i][j] = dp[i - 1][j];
19                p[i][j] = 3;
20            }
21        }
22    }
23    string ans = "";
24    int ni = lens, nj = lent;
25    while (ni && nj) {
26        if (p[ni][nj] == 1) {
27            ans = s[ni - 1] + ans;
28            ni--, nj--;
29        } else if (p[ni][nj] == 2) nj--;
30        else ni--;
31    }
32    cout << ans << "\n";
33    return 0;
34}

G. Longest Path

類似拓樸排序的方式在 DAG(有向無環圖)上做 dp。

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 100005
 4int main() {
 5    int n, m; scanf("%d%d", &n, &m);
 6    int in[maxn] = {}, dp[maxn] = {};
 7    vector<int> e[maxn];
 8    for (int i = 0; i < m; i++) {
 9        int a, b; scanf("%d%d", &a, &b);
10        e[a].push_back(b);
11        in[b]++;
12    }
13    queue<int> q;
14    for (int i = 1; i <= n; i++)
15        if (!in[i]) q.push(i);
16    while (!q.empty()) {
17        int u = q.front(); q.pop();
18        for (int uu: e[u]) {
19            in[uu]--;
20            dp[uu] = max(dp[uu], dp[u] + 1);
21            if (!in[uu]) q.push(uu);
22        }
23    }
24    int ans = 0;
25    for (int i = 1; i <= n; i++)
26        ans = max(ans, dp[i]);
27    printf("%d\n", ans);
28    return 0;
29}

H. Grid 1

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define mod 1000000007
 4int main() {
 5    int h, w; scanf("%d%d", &h, &w);
 6    int dp[1005] = {};
 7    char c[1005];
 8    for (int i = 1; i <= h; i++) {
 9        scanf("%s", c + 1);
10        for (int j = 1; j <= w; j++)
11            if (c[j] == '#') dp[j] = 0;
12            else if (i == 1 && j == 1) dp[j] = 1;
13            else dp[j] = (dp[j] + dp[j - 1]) % mod;
14    }
15    printf("%d\n", dp[w]);
16    return 0;
17}

I. Coins

機率的 dp,所以難得用到 double。

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 3000
 4int main() {
 5    int n; scanf("%d", &n);
 6    double dp[maxn] = {1.0};
 7    for (int i = 1; i <= n; i++) {
 8        double p; scanf("%lf", &p);
 9        for (int j = min(i, n / 2); j > 0; j--)
10            dp[j] = dp[j] * (1 - p) + dp[j - 1] * p;
11        dp[0] = dp[0] * (1 - p);
12    }
13    double ans = 1;
14    for(int i = 0; i <= n / 2; i++)
15        ans -= dp[i];
16    printf("%.9f\n", ans);
17    return 0;
18}

J. Sushi

i 為剩 3 個,j 為剩 2 個,k 為剩 1 個的盤子數量。

$$DP_{i,j,k} = 1 + \frac{(n-i-j-k) DP_{i,j,k} + i DP_{i-1,j+1,k} + j DP_{i,j-1,k+1} + k DP_{i,j,k-1}}{n}$$

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 305
 4double dp[maxn][maxn][maxn];
 5inline double dfs(int i, int j, int k, int n) {
 6    if(i < 0 || j < 0 || k < 0) return 0;
 7    if (dp[i][j][k] > -1) return dp[i][j][k];
 8    return dp[i][j][k] = (i * dfs(i - 1, j + 1, k, n) +
 9                          j * dfs(i, j - 1, k + 1, n) + 
10                          k * dfs(i, j, k - 1, n) +
11                          n) / (i + j + k);
12}
13int main() {
14    int n; scanf("%d", &n);
15    int cnt[4] = {};
16    for (int i = 0; i < n; i++) {
17        int x; cin >> x;
18        cnt[x]++;
19    }
20    for (int i = 0; i <= n; i++)
21        for (int j = 0; j <= n; j++)
22            for (int k = 0; k <= n; k++)
23                dp[i][j][k] = -10;
24    dp[0][0][0] = 0;
25    printf("%.9f\n", dfs(cnt[3], cnt[2], cnt[1], n));
26    return 0;
27}

K. Stones

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 105
 4#define maxk 100005
 5int main() {
 6    int n, k; scanf("%d%d", &n, &k);
 7    int a[maxn];
 8    bool dp[maxk] = {0};
 9    for (int i = 0; i < n; i++)
10        scanf("%d", &a[i]);
11    for (int i = 0; i <= k; i++)
12        if(!dp[i])
13            for (int j = 0; j < n; j++)
14                if(i + a[j] <= k)
15                    dp[i + a[j]] = true;
16    if (dp[k]) printf("First\n");
17    else printf("Second\n");
18    return 0;
19}

L. Deque

第一次遇到這題是在 2019NCPC,當時思考了很久能不能 Greedy,後來才想到是 0-sum game。於是用了 minimax 的概念,設計出狀態。

 1#include<bits/stdc++.h>
 2using namespace std;
 3typedef long long LL;
 4int main() {
 5    ios_base::sync_with_stdio(0), cin.tie(0);
 6    int n; cin >> n;
 7    vector<LL> v(n+2);
 8    for (int i = 1; i <= n; i++) cin >> v[i];
 9    vector<vector<LL> > dp(n + 2, vector<LL>(n + 2,0));
10    for (int len = 1; len <= n; len++)
11        for (int i = 1, j = len; j <= n; i++, j++)
12            if(len % 2 == n % 2)
13                dp[i][j] = max(dp[i + 1][j] + v[i], 
14                               dp[i][j - 1] + v[j]);
15            else
16                dp[i][j] = min(dp[i + 1][j] - v[i], 
17                               dp[i][j - 1] - v[j]);
18    cout << dp[1][n] << "\n";
19    return 0;
20}

M. Candies

dp[i][j] 發到第 i 人剩下 j 顆糖的方法數,用滾動優化空間複雜度。

dp[i][j] = dp[i-1][j] + … + dp[i-1][j+a[i]]。

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxk 100005 
 4#define maxn 105
 5#define mod 1000000007
 6int main() {
 7    int n, k; scanf("%d%d", &n, &k);
 8    int a[maxn], dp[2][maxk];
 9    for (int i = 1; i <= n; i++)
10        scanf("%d", &a[i]);
11    dp[0][k] = 1;
12    for(int i = 1; i <= n; i++) {
13        int sum = 0;
14        for(int j = k; j >= 0; j--) {
15            sum = (sum + dp[(i + 1) & 1][j]) % mod;
16            if(j + a[i] + 1 <= k)
17                sum = (sum + mod 
18                           - dp[(i + 1) & 1][j + a[i] + 1]) % mod;
19            dp[i & 1][j] = sum;
20        }
21    }
22    printf("%d\n", dp[n & 1][0]);
23    return 0;   
24}

N. Slimes

dp[i][j] 為合併史萊姆 i ~ j 所需的最小 cost。

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 405
 4#define INF 100000000000000
 5typedef long long LL;
 6int main() {
 7    int n; scanf("%d", &n);
 8    int a[maxn];
 9    for (int i = 1; i <= n; i++)
10        scanf("%d", &a[i]);
11    LL sum[maxn] = {};
12    LL dp[maxn][maxn] = {};
13    for (int i = 1; i <= n; i++)
14        sum[i] = sum[i - 1] + a[i];
15    for (int len = 2; len <= n ; len++)
16        for(int i = 1, j = i + len - 1; j <= n; i++, j++) {
17            dp[i][j] = INF;
18            for (int k = i; k < j; k++)
19                dp[i][j] = min(dp[i][j],
20                   dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]);
21        }
22    printf("%lld\n", dp[1][n]);
23    return 0;
24}

O. Matching

位元 dp。

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 23
 4#define maxm (1<<21)+5
 5#define mod 1000000007
 6int dp[maxm] = {1};
 7int main() {
 8    int n; scanf("%d", &n);
 9    int a[maxn][maxn];
10    for (int i = 0; i < n; i++)
11        for (int j = 0; j < n; j++)
12            scanf("%d", &a[i][j]);
13    int M = (1 << n) - 1;
14    for (int j = 0; j <= M; j++) {
15        int i = __builtin_popcount(j) - 1;
16        for (int k = 0; k < n; k++)
17            if (j & (1 << k) && a[i][k])
18                dp[j] = (dp[j] + dp[j - (1 << k)]) % mod;
19    }
20    printf("%d\n", dp[M]);
21    return 0;
22}

P. Independent Set

經典樹 dp。

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 100005
 4#define mod 1000000007
 5vector<int> e[maxn];
 6long long dp[maxn][2] = {};
 7void dfs(int v, int p) {
 8    dp[v][0] = dp[v][1] = 1;
 9    for (int u : e[v]) {
10        if (u == p) continue;
11        dfs(u, v);
12        dp[v][0] = dp[v][0] * (dp[u][0] + dp[u][1]) % mod;
13        dp[v][1] = dp[v][1] * dp[u][0] % mod;
14    } 
15}
16int main() {
17    int n; scanf("%d", &n);
18    for (int i = 1; i < n; i++) {
19        int a, b; scanf("%d%d", &a, &b);
20        e[a].push_back(b), e[b].push_back(a);
21    }
22    dfs(1, 0);
23    printf("%lld\n", (dp[1][1] + dp[1][0]) % mod);
24    return 0;
25}

Q. Flowers

帶權最長遞增子序列(Weighted Longest Increasing Subsequence)。

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 200005
 4typedef long long LL;
 5int main() {
 6    int n; scanf("%d", &n);
 7    int h[maxn], a[maxn];
 8    for (int i = 0; i < n; i++) scanf("%d", &h[i]);
 9    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
10    map<int, LL> m; m[0] = 0;
11    for(int i = 0; i<n; i++) {
12        map<int,LL>::iterator iter, iter_tmp;
13        iter = m.lower_bound(h[i]);
14        iter--;
15        LL val = iter->second + a[i];
16        iter++;
17        while(iter != m.end() && iter->second <= val) {
18            iter_tmp = iter;
19            iter_tmp++;
20            m.erase(iter);
21            iter = iter_tmp;
22        }
23        m[h[i]] = val;
24    }
25    printf("%lld\n", m.rbegin()->second);
26    return 0;
27}

R. Walk

矩陣快速冪。

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define maxn 55
 4#define mod 1000000007
 5typedef long long LL;
 6struct matrix {
 7    int sz;
 8    int m[maxn][maxn];
 9    matrix(){}
10    matrix(int n) : sz(n) { memset(m, 0, sizeof(m)); }
11    void operator = (const matrix &A) {
12        for (int i = 0; i < sz; i++)
13            for (int j = 0; j < sz; j++)
14                m[i][j] = A.m[i][j];
15    }
16    matrix operator*(const matrix &A) {
17        matrix B(sz);
18        for (int i = 0; i < sz; i++)
19            for (int j = 0; j < sz; j++) {
20                B.m[i][j] = 0;
21                for (int k = 0; k < sz; k++)
22                    B.m[i][j] = ((LL)m[i][k] * A.m[k][j] % mod
23                                 + B.m[i][j]) % mod;
24            }
25        return B;
26    }
27};
28matrix pow(matrix A, LL k) {
29    matrix ans(A.sz);
30    for (int i = 0; i < A.sz; i++)
31        ans.m[i][i] = 1;
32    while (k) {
33        if (k & 1) ans = ans * A;
34        k >>= 1;
35        A = A * A;
36    }
37    return ans;
38}
39int main() {
40    int n;
41    LL k;
42    scanf("%d%lld", &n, &k);
43    matrix A(n);
44    for (int i = 0; i < n; i++)
45        for (int j = 0; j < n; j++)
46            scanf("%d", &A.m[i][j]);
47    matrix ans = pow(A, k);
48    int cnt = 0;
49    for (int i = 0; i < n; i++)
50        for (int j = 0; j < n; j++)
51            cnt = (cnt + ans.m[i][j]) % mod;
52    printf("%d\n", cnt);
53    return 0;
54}

S. Digit Sum

dp[i][j][k]:到第 i 個字母,同餘 j 的方法數,k 代表前 i 位是否和 input 的前 i 位一致。

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define mod 1000000007
 4int dp[2][102][2] = {};
 5inline void add(int &a, int b) {
 6    a = (a + b % mod) % mod;
 7}
 8int main() {
 9    ios_base::sync_with_stdio(0);
10    cin.tie(0);
11    string s; int d;
12    cin >> s >> d;
13    int len = s.length();
14    dp[1][0][1] = 1;
15    for (int i = 0; i < len; i++) {
16        memset(dp[i & 1], 0, sizeof(dp[i & 1]));
17        for (int j = 0; j < d; j++)
18            for (int k = 0; k <= 9; k++)
19                if(k < s[i]-'0')
20                    add(dp[i & 1][(j + k) % d][0], 
21                        dp[!(i & 1)][j][0]),
22                    add(dp[i & 1][(j + k) % d][0], 
23                        dp[!(i & 1)][j][1]);
24                else if (k == s[i] - '0')
25                    add(dp[i & 1][(j + k) % d][0], 
26                        dp[!(i & 1)][j][0]),
27                    add(dp[i & 1][(j + k) % d][1], 
28                        dp[!(i & 1)][j][1]);
29                else
30                    add(dp[i & 1][(j + k) % d][0], 
31                        dp[!(i & 1)][j][0]);
32    }
33    cout << (dp[!(len & 1)][0][0] + 
34             dp[!(len & 1)][0][1] - 1 + mod) % mod << "\n";
35    return 0;
36}

T. Permutation

dp[i][j] 定義為前 i + 1 個數字結尾為 j 的組合數為多少(1 ≤ j ≤ i + 1)。 前綴和加速到 O(n²)。

 1#include<bits/stdc++.h>
 2using namespace std;
 3const int mod = 1000000007;
 4int main() {
 5    ios_base::sync_with_stdio(0),cin.tie(0);
 6    int n;
 7    string s;
 8    cin >> n >> s;
 9    vector<int> dp(n + 1, 0), sum(n + 1, 1);
10    dp[1] = 1, sum[0] = 0;
11    for (int i = 1; i < n; i++) {
12        for (int j = 1; j <= i + 1; j++) {
13            if (s[i - 1] == '>')
14                dp[j] = (sum[i] - sum[j - 1] + mod) % mod;
15            else
16                dp[j] = sum[j - 1];
17        }
18        for (int j = 1; j <= n; j++)
19            sum[j] = (sum[j - 1] + dp[j]) % mod;
20    }
21    cout << sum[n] << "\n";
22    return 0;
23}

U. Grouping

學會枚舉子集的辦法了!!

 1#include<bits/stdc++.h>
 2using namespace std;
 3typedef long long ll;
 4int main() {
 5    ios_base::sync_with_stdio(0), cin.tie(0);
 6    int n; cin >> n;
 7    vector<vector<int>> a(n, vector<int>(n));
 8    for (int i = 0; i < n; i++)
 9        for (int j = 0; j < n; j++)
10            cin >> a[i][j];
11    int m = (1 << n);
12    vector<ll> cnt(m), dp(m);
13    for (int s = 0; s < m; s++)
14        for (int i = 0; i < n; i++)
15            if (s & (1 << i))
16                for (int j = i; j < n; j++)
17                    if (s & (1 << j))
18                        cnt[s] += a[i][j];
19    for (int s = 0; s < m; s++)
20        for (int sub = s; sub > 0; sub = (sub - 1) & s)
21            dp[s] = max(dp[s], dp[s - sub] + cnt[sub]);
22    cout << dp[m - 1] << "\n";
23    return 0;
24}

V. Subtree

參考 codeforces 這篇討論 才寫出來。

解法:(有根樹)。

  • up/down[node] 是 node 以上/下的點塗黑的方法數。
  • down 的不難算,直接 dfs 做樹 dp 就行了。
  • up 需要用到 down 的結果,算如果取 parent,其他子樹 down 的方法數。
  • +1 代表不取 parent。
  • 最後各點的結果為 up * down。
  • 算其他子樹的方法和,因為需要取 mod 且模數不為質數,故需要維護其前/後綴積。
 1#include<bits/stdc++.h>
 2using namespace std;
 3typedef long long ll;
 4#define maxn 100005
 5#define pb push_back
 6int mod;
 7vector<int> e[maxn];
 8vector<ll> pre[maxn], suf[maxn];
 9ll up[maxn], down[maxn];
10void dfs1(int u, int p) {
11    for (int uu : e[u])
12        if (uu != p) dfs1(uu, u);
13    for (int i = 0; i < e[u].size(); i++) {
14        int uu = e[u][i];
15        pre[u][i] = i ? pre[u][i-1] : 1;
16        if(uu != p)
17            pre[u][i] = pre[u][i] * (1 + down[uu]) % mod;
18    }
19    for (int i = e[u].size() - 1; i >= 0; i--) {
20        int uu = e[u][i];
21        suf[u][i] = (i < e[u].size() - 1) ? suf[u][i + 1] : 1;
22        if(uu != p)
23            suf[u][i] = suf[u][i] * (1 + down[uu]) % mod;
24    }
25    down[u] = suf[u][0];
26}
27void dfs2(int u, int p) {
28    for (int i = 0; i < e[u].size(); i++) {
29        int uu = e[u][i];
30        if (uu == p) continue;
31        ll cnt = up[u];
32        if(i)
33            cnt = cnt * pre[u][i - 1] % mod;
34        if(i + 1 < e[u].size())
35            cnt = cnt * suf[u][i + 1] % mod;
36        up[uu] = cnt + 1; // 1是不取par
37        dfs2(uu, u);
38    }
39}
40int main() {
41    ios_base::sync_with_stdio(0);
42    cin.tie(0);
43    int n; cin >> n >> mod;
44    for (int i = 1; i < n; i++) {
45        int a, b; cin >> a >> b;
46        e[a].pb(b), e[b].pb(a);
47    }
48    e[1].pb(0), up[1] = 1;
49    for (int i = 1; i <= n; i++) {
50        pre[i].resize(e[i].size());
51        suf[i].resize(e[i].size());
52    }
53    dfs1(1, 0), dfs2(1, 0);
54    for (int i = 1; i <= n; i++)
55        cout << down[i] * up[i] % mod << "\n";
56    return 0;
57}

W. Intervals

  • 題意: 給定 M (1 ≤ M ≤ 200000) 組 (l[i], r[i]) (1 ≤ l[i] ≤ r[i] ≤ N ≤ 200000) 和 a[i] (|a[i]| ≤ 1000000000)。 當 l[i] ~ r[i] 至少有一個 1 的 a[i] 分,問構造出長度 N 的 01 字串最高得幾分。
  • 解法:
    • 轉移式:dp[i] = max{ dp[j] + sum{a[k]} }, j < l[k] ≤ i ≤ r[k]
    • 優化:區間加值線段樹維護最大值。
 1#include<bits/stdc++.h>
 2using namespace std;
 3typedef long long ll;
 4typedef pair<int, ll> pil;
 5#define ls i << 1
 6#define rs i << 1 | 1
 7class RangeUpdateSegmentTree {
 8  private:
 9    struct node {
10        int l, r;
11        ll x = 0, adt = 0;
12    };
13    vector<node> a;
14    void push(int i) {
15        if (a[i].adt) {
16            a[ls].adt += a[i].adt, a[rs].adt += a[i].adt;
17            a[ls].x += a[i].adt, a[rs].x += a[i].adt;
18            a[i].adt = 0;
19        }
20    }
21    void pull(int i) {
22        a[i].x = max(a[ls].x, a[rs].x);
23    }
24    void build(int l, int r, int i) {
25        a[i].l = l, a[i].r = r;
26        if (l == r) return;
27        int mid = (l + r) >> 1;
28        build(l, mid, ls), build(mid + 1, r, rs);
29    }
30  public:
31    RangeUpdateSegmentTree(int n) : a(n << 2) {
32        build(0, n, 1);
33    }
34    void add(int l, int r, ll val, int i = 1) {
35        if (a[i].l >= l && a[i].r <= r) {
36            a[i].x += val;
37            a[i].adt += val;
38            return;
39        }
40        push(i);
41        int mid = (a[i].l + a[i].r) >> 1;
42        if (l <= mid) add(l, r, val, ls);
43        if (r > mid) add(l, r, val, rs);
44        pull(i);
45    }
46    ll maxx(int l, int r, int i = 1) {
47        if (l <= a[i].l && a[i].r <= r) return a[i].x;
48        push(i);
49        ll ret = -9e18;
50        int mid = (a[i].l + a[i].r) >> 1;
51        if (l <= mid) ret = max(ret, maxx(l, r, ls));
52        if (r > mid) ret = max(ret, maxx(l, r, rs));
53        pull(i);
54        return ret;
55    }
56};
57int main() {
58    ios_base::sync_with_stdio(0);
59    cin.tie(0);
60    int n, m;
61    cin >> n >> m;
62    vector<ll> add(n + 1);
63    vector<vector<pil>> del(n + 1);
64    for (int i = 0; i < m; i++) {
65        int l, r; ll a;
66        cin >> l >> r >> a;
67        add[l] += a;
68        del[r].push_back(pil(l, a));
69    }
70    RangeUpdateSegmentTree ST(n + 1);
71    ll ans = 0;
72    for (int i = 1; i <= n; i++) {
73        ST.add(0, i - 1, add[i]);
74        ll tmp = ST.maxx(0, i - 1);
75        ans = max(ans, tmp);
76        ST.add(i, i, tmp);
77        for (pil p : del[i])
78            ST.add(0, p.first - 1, -p.second);
79    }
80    cout << ans << "\n";
81    return 0;
82}

X. Tower

將 solidness 排序後,再做 dp。

 1#include<bits/stdc++.h>
 2using namespace std;
 3typedef long long LL;
 4typedef pair<int,int> pii;
 5typedef pair<int,pii> box;
 6#define s first
 7#define w second.first
 8#define v second.second
 9#define maxn 1005
10#define maxs 20005
11int main() {
12    int n; scanf("%d",&n);
13    box b[maxn];
14    for(int i=0; i<n; i++){
15        scanf("%d%d%d", &b[i].w, &b[i].s, &b[i].v);
16        b[i].s += b[i].w;
17    }
18    sort(b, b + n);
19    LL dp[maxs] = {};
20    LL ans = 0;
21    for (int i = 0; i < n; i++) {
22        for (int j = b[i].s; j >= b[i].w; j--) {
23            dp[j] = max(dp[j], dp[j - b[i].w] + b[i].v);
24            ans = max(ans, dp[j]);
25        }
26    }
27    printf("%lld\n", ans);
28    return 0;
29}

Y. Grid 2

如果沒有障礙物,答案會是 C(h - 1 + w - 1, h - 1)。 有障礙物,就將障礙物那格的答案扣掉 C(x[i] - x[j] + y[i] - y[j], x[i] - x[j]) 次。

 1#include<bits/stdc++.h>
 2using namespace std;
 3typedef pair<int, int> pii;
 4typedef long long ll;
 5#define x first
 6#define y second
 7#define mod 1000000007
 8#define LEN 200005
 9ll frc[LEN], inv[LEN];
10ll modinv(ll a, ll p = mod) {
11    if (p == 1) return 0;
12    ll pp = p, y = 0, x = 1;
13    while (a > 1) {
14        ll q = a / p, t = p;
15        p = a % p, a = t, t = y, y = x - q * y, x = t;
16    }
17    if (x < 0) x += pp;
18    return x;
19}
20void init() {
21    frc[0] = frc[1] = inv[0] = inv[1] = 1; 
22    for (int i = 2; i < LEN; i++) {
23        frc[i] = frc[i - 1] * i % mod;
24        inv[i] = modinv(frc[i]);
25    }
26}
27inline ll C(int a, int b) {
28    return frc[a + b] * inv[a] % mod * inv[b] % mod;
29}
30inline void sub(ll &a, ll b) {
31    a = (a - b % mod + mod) % mod;
32}
33int main() {
34    ios_base::sync_with_stdio(0);
35    cin.tie(0);
36    init();
37    int h, w, n;
38    cin >> h >> w >> n;
39    vector<pii> p(n);
40    for (int i = 0; i < n; i++)
41        cin >> p[i].x >> p[i].y;
42    sort(p.begin(), p.end());
43    p.push_back(pii(h, w));
44    vector<ll> dp(n + 1, 0);
45    for (int i = 0; i <= n; i++) {
46        dp[i] = C(p[i].x - 1, p[i].y - 1);
47        for (int j = 0; j < i; j++)
48            if (p[i].y >= p[j].y)
49                sub(dp[i], dp[j] * C(p[i].x - p[j].x, p[i].y - p[j].y));
50    }
51    cout << dp[n] << "\n";
52    return 0;
53}

Z. Grid 3

h[i] 為遞增,故可斜率優化,總複雜度 O(N)。

 1#include<bits/stdc++.h>
 2using namespace std;
 3typedef long long ll;
 4int main() {
 5    ios_base::sync_with_stdio(0), cin.tie(0);
 6    int n; ll C; cin >> n >> C;
 7    vector<ll> h(n + 1);
 8    for (int i = 1; i <= n; i++) cin >> h[i];
 9    vector<ll> dp(n + 1);
10    deque<int> dq(1, 1);
11    auto X = [&](int i) {
12        return -2 * h[i];
13    };
14    auto Y = [&](int i) {
15        return h[i] * h[i] + dp[i];
16    };
17    auto F = [&](int i, int j) {
18        return h[i] * X(j) + Y(j) + C + h[i] * h[i];
19    };
20    auto cross = [](ll x1, ll y1, ll x2, ll y2) {
21        return x1 * y2 - x2 * y1;
22    };
23    for (int i = 2; i <= n; i++) {
24        while (dq.size() >= 2) {
25            if (F(i, dq[0]) >= F(i, dq[1]))
26                dq.pop_front();
27            else break;
28        }
29        dp[i] = F(i, dq[0]);
30        while (dq.size() >= 2) {
31            int j = dq.back(), k = dq[dq.size()-2];
32            if (cross(X(j)-X(k), Y(j)-Y(k), X(i)-X(j), Y(i)-Y(j)) >= 0)
33                dq.pop_back();
34            else break;
35        }
36        dq.push_back(i);
37    }
38    cout << dp[n] << "\n";
39    return 0;
40}
Built with Hugo
Theme Stack designed by Jimmy