2022杭电多校杂补
导航
第七场 F. Sumire 弱智的数位DP
第七场 G. Weighted Beautiful Tree 有意思的树形DP
第七场
F. Sumire
思路
- 记录状态 \(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
- 转移题解写的很清楚了。
- 这道题的特别之处是他的状态和实现转移的方式,简单的观察点是节点值一定和某个邻边值相同(也有可能是他原值或者父边的值)
- \(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;
}