2022杭电多校杂补

导航

第七场 F. Sumire 弱智的数位DP
第七场 G. Weighted Beautiful Tree 有意思的树形DP

第七场

题目链接

F. Sumire

数位DP

思路

  • 记录状态 \(dp_{rem,pre}\) 剩余 \(rem\) 位,前面有 \(pre\) 个数位 d。
  • 转移的时候不能枚举 B 个数,发现只有数位 = d 的时候对 pre 有更新,所以直接讨论就好了。
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef double db;
#define re _read
#define ALL(x) (x).begin(),(x).end()
#define SZ(v) ((int)v.size())
#define fi first
#define se second
#define pb push_back
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
using namespace std;
mt19937 mrand(random_device{}()); 
int rnd(int x) { return mrand() % x;}
template<class T>
inline void _read(T& x) {
    static T ans;
    static unsigned int c;
    static bool p;
    for (c = getchar(); c != '-' && (c < '0' || c > '9'); c = getchar());
    if (c == '-') p = false, c = getchar(); else p = true;
    for (ans = 0; c <= '9' && c >= '0'; c = getchar()) ans = ans * 10 + c - '0';
    x = p ? ans : -ans;
}
/*----------------------------------------------------------------------------------------------------*/
const int mod = 1e9 + 7;

ll qmi(ll a, ll k, int mod) {
    ll res = 1;
    if (a == 0 && k == 0) return 0;
    while (k) {
            if (k & 1)
                    res = res * a % mod;
            a = a * a % mod;
            k >>= 1;
    }
    return res;
}

ll k, B, D, l, r;
ll dp[70][70];

ll dfs(int rem, int pre) {
    ll& ans = dp[rem][pre];
    if (ans != -1) return ans;
    ans = 0;
    if (!rem) return ans = qmi(pre, k, mod);
    ans = dfs(rem - 1, pre + 1) + (B - 1) * dfs(rem - 1, pre) % mod;
    if (ans >= mod) ans -= mod;
    return ans;
}

ll solve(ll x) {
    x ++;
    vector<int> d;
    while (x) {
        d.pb(x % B);
        x /= B;
    }
    reverse(ALL(d));
    int m = d.size();
    ll ans = 0;
    for (int i = 1; i < m; i++) {
        ans += (B - 2) * dfs(i - 1, 0) % mod;
        if (ans >= mod) ans -= mod;
        ans += dfs(i - 1, D != 0);
        if (ans >= mod) ans -= mod;
    }
    int pre = 0;
    for (int i = 0; i < m; i++) {
        if (i == 0) {
            if (!D) {
                ans += (d[i] - 1) * dfs(m - i - 1, pre) % mod;
            }
            else {
                if (d[i] > D) ans += (dfs(m - i - 1, pre + 1) + (d[i] - 2) * dfs(m - i - 1, pre) % mod) % mod;
                else ans += (d[i] - 1) * dfs(m - i - 1, pre) % mod;
            }
            if (ans >= mod) ans -= mod;
        }
        else {
            if (d[i] > D) ans += (dfs(m - i - 1, pre + 1) + (d[i] - 1) * dfs(m - i - 1, pre) % mod) % mod;
            else ans += d[i] * dfs(m - i - 1, pre) % mod;
            if (ans >= mod) ans -= mod;
        }
        if (d[i] == D) pre++;
    }
    return ans;
}

int main() {
    int T;
    re(T);
    while (T--) {
        memset(dp, -1, sizeof dp);
        re(k), re(B), re(D), re(l), re(r);
        printf("%lld\n", (solve(r) - solve(l - 1) + mod) % mod);
    }
    return 0;
}

G. Weighted Beautiful Tree

树形DP

  • 转移题解写的很清楚了。
  • 这道题的特别之处是他的状态和实现转移的方式,简单的观察点是节点值一定和某个邻边值相同(也有可能是他原值或者父边的值
  • \(f[u][0/1]\) 表示 u 节点权值小于父边权值,大于父边权值的最小花费。
#include<bits/stdc++.h>
// #pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef double db;
#define re _read
#define ALL(x) (x).begin(),(x).end()
#define SZ(v) ((int)v.size())
#define fi first
#define se second
#define pb push_back
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
using namespace std;
mt19937 mrand(random_device{}()); 
int rnd(int x) { return mrand() % x;}
template<class T>
inline void _read(T& x) {
    static T ans;
    static unsigned int c;
    static bool p;
    for (c = getchar(); c != '-' && (c < '0' || c > '9'); c = getchar());
    if (c == '-') p = false, c = getchar(); else p = true;
    for (ans = 0; c <= '9' && c >= '0'; c = getchar()) ans = ans * 10 + c - '0';
    x = p ? ans : -ans;
}
/*----------------------------------------------------------------------------------------------------*/
const int N = 1e5 + 10;
const ll INF = 2e18;
vector<PII> edge[N];
int n, cost[N], wn[N];
ll faw[N];
ll f[N][2];


// dp[u][0/1] 低于 父边权值,高于 父边权值
// 枚举当前点权值为 j (所有边的取值)
// 对于每个 j 的转移代价。
// val = abs(j - wn[u])
// val += dp[v][0], j > e
// val += min(dp[v][0], dp[v][1]), j = e;
// val += dp[v][1], j < e

// dp[u][0] = min({val}), j <= faw[u]
// dp[u][1] = min({val}), j >= faw[u]

void dfs(int u, int p) {
    vector<PII> son;
    ll sum0 = 0, sum1 = 0;
    f[u][0] = f[u][1] = INF;
    bool is_leaf = true;
    son.pb({faw[u], 0});
    son.pb({wn[u], 0});
    for (auto t: edge[u]) {
        int v = t.fi, w = t.se;
        if (v == p) continue;
        faw[v] = w;
        dfs(v, u);
        is_leaf = false;
        son.pb({w, v});
        sum1 += f[v][1];
    }
    if (is_leaf) {
        f[u][0] = wn[u] <= faw[u] ? 0 : 1ll * abs(wn[u] - faw[u]) * cost[u];
        f[u][1] = wn[u] >= faw[u] ? 0 : 1ll * abs(wn[u] - faw[u]) * cost[u];
        return;
    }
    sort(ALL(son));
    for (int i = 0; i < SZ(son); ) {
        int w = son[i].fi, v = son[i].se;
        ll add_sum0 = 0, val = 1ll * abs(wn[u] - w) * cost[u];
        int j = i;
        for (j = i; j < SZ(son);) {
            if (son[i].first == son[j].first) {
                sum1 -= f[son[j].second][1];
                add_sum0 += f[son[j].second][0];
                val += min(f[son[j].se][0], f[son[j].se][1]);
                j++;
            }
            else break;
        }
        if (w >= faw[u]) f[u][1] = min(f[u][1], val + sum0 + sum1);
        if (w <= faw[u]) f[u][0] = min(f[u][0], val + sum0 + sum1);
        sum0 += add_sum0;
        i = j;
    }
}

int main() {
    // freopen("1007.in", "r", stdin);
    // freopen("output.txt", "w", stdout);
    int T;
    scanf("%d", &T);
    while (T--) {
        re(n);
        for (int i = 1; i <= n; i++) re(cost[i]), edge[i].clear();
        for (int i = 1; i <= n; i++) re(wn[i]);
        for (int i = 1; i < n; i++) {
            int u, v, w;
            re(u), re(v), re(w);
            edge[u].pb({v, w});
            edge[v].pb({u, w});
        }
        for (int i = 1; i <= n; i++) {
            sort(ALL(edge[i]), [&](PII a, PII b) {
                if (a.second != b.second) return a.second < b.second;
                return a.first < b.first;
            });
        }
        faw[1] = wn[1];
        dfs(1, 0);
        printf("%lld\n", min(f[1][0], f[1][1]));
    }
    // fclose(stdin);
    // fclose(stdout);
    return 0;
}
posted @ 2022-10-27 18:29  Roshin  阅读(42)  评论(0编辑  收藏  举报
-->