花开堪折直须折,莫待无花空折枝。|

cn_ryh

园龄:2年2个月粉丝:0关注:5

CF1394D 题解

CF1394D Boboniu and Jianghu 题解

Luogu

Codeforces

Solution

可以处理出对于每个点,向下 b 递增的最小 a 之和 dpi,0 和向下 b 递减的最小 a 之和 dpi,1。令与一个点 u 相连的点中有 k1b 值大于点 u 的,剩余 k2 个小于点 u 的,则该点贡献为 max(k1,k2)×au

对于个端点指出的边,若两端点的 b 值相等则既可以连接子树递增的链,又可以连接子树递减的链。我们先假设连接递增的链,然后贪心的改变一些链接情况。具体的,找到从链接递增改变到连接递减后改变最大的一部分进行改变。

利用 dfs 进行转移即可。

该部分代码:

void dfs(int u, int fa)
{
int sum = 0, cnt_up = 0, cnt_down = 0;
for (int i = head[u]; i; i = edge[i].nxt)
{
int v = edge[i].to;
if (v == fa)
{
continue;
}
dfs(v, u);
// 可以递增,可以递减
if (points[u].b == points[v].b)
{
// 换一个的价值
st[u].push_back(dp[v][1] - dp[v][0]);
// 先假定递增
++cnt_up;
sum += dp[v][0];
}
else if (points[u].b > points[v].b)
{
++cnt_up;
sum += dp[v][0];
}
else
{
++cnt_down;
sum += dp[v][1];
}
}
sort(st[u].begin(), st[u].end());
// 把多少个转换
for (int i = 0; i <= st[u].size(); i++)
{
dp[u][0] = min(dp[u][0], sum + max(cnt_up, cnt_down + (u != 1)) * points[u].a);
dp[u][1] = min(dp[u][1], sum + max(cnt_up + (u != 1), cnt_down) * points[u].a);
--cnt_up, ++cnt_down; // 改变了一条边
if (i < st[u].size())
{
sum += st[u][i];
}
}
}

注意到当 u=1 时需要特殊转移,即两条链可以合并为一条。最终答案即为 min(dp1,0,dp1,1)

Codes

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define max_n 210101
void read(int &p)
{
p = 0;
int k = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
{
k = -1;
}
c = getchar();
}
while (c >= '0' && c <= '9')
{
p = p * 10 + c - '0';
c = getchar();
}
p *= k;
return;
}
void write_(int x)
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x > 9)
{
write_(x / 10);
}
putchar(x % 10 + '0');
}
void writesp(int x)
{
write_(x);
putchar(' ');
}
void writeln(int x)
{
write_(x);
putchar('\n');
}
int n;
struct node
{
int to, nxt;
} edge[max_n << 1];
int head[max_n], tot;
void add(int u, int v)
{
edge[++tot].to = v;
edge[tot].nxt = head[u];
head[u] = tot;
}
struct Point
{
int a, b;
} points[max_n];
int dp[max_n][2];
vector<int> st[max_n];
void dfs(int u, int fa)
{
int sum = 0, cnt_up = 0, cnt_down = 0;
for (int i = head[u]; i; i = edge[i].nxt)
{
int v = edge[i].to;
if (v == fa)
{
continue;
}
dfs(v, u);
// 可以递增,可以递减
if (points[u].b == points[v].b)
{
// 换一个的价值
st[u].push_back(dp[v][1] - dp[v][0]);
// 先假定递增
++cnt_up;
sum += dp[v][0];
}
else if (points[u].b > points[v].b)
{
++cnt_up;
sum += dp[v][0];
}
else
{
++cnt_down;
sum += dp[v][1];
}
}
sort(st[u].begin(), st[u].end());
// 把多少个转换
for (int i = 0; i <= st[u].size(); i++)
{
dp[u][0] = min(dp[u][0], sum + max(cnt_up, cnt_down + (u != 1)) * points[u].a);
dp[u][1] = min(dp[u][1], sum + max(cnt_up + (u != 1), cnt_down) * points[u].a);
--cnt_up, ++cnt_down; // 改变了一条边
if (i < st[u].size())
{
sum += st[u][i];
}
}
}
signed main()
{
#if _clang_
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
read(n);
for (int i = 1; i <= n; i++)
{
read(points[i].a);
}
for (int i = 1; i <= n; i++)
{
read(points[i].b);
}
for (int i = 1, u, v; i < n; i++)
{
read(u), read(v);
add(u, v);
add(v, u);
}
memset(dp, 0x3f, sizeof(dp));
dfs(1, 0);
writeln(min(dp[1][0], dp[1][1]));
return 0;
}
posted @   cn_ryh  阅读(12)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起