Decay of Signals【2020 年 “游族杯” D题】【树形dp】

题目链接


  这题有一个规律的东西,就是我们实际上只需要看1、2的链,譬如说有一个大于2权值为k的点,如果它的周围有权值为1的点的话,那么我们实际上没有必要去选上k,只需要选1即可,因为若是选上权值为k的话,k > 2,那么需要使得要取的值为1的点的个数为x的情况下:\frac{k}{k * x + 1} < \frac{1}{x},又有k > 2,所以必须存在一条存在点度大于2的链,显然,这是不现实的,所以我们只需要考虑1、2两个点就是了,0的时候答案自然为“0/1”,若是没有1、2和0那肯定就是每个点“minn/1”就是了。

  所以,关于2,还是要特殊的,\frac{2}{2 * x + 1} < \frac{1}{x}需要保证的是,2的两边的x数量是对等的,若是有一边偏大,那么直接选那一边不是更好吗?

  然后我们维护这个式子\frac{2}{2 * x + 1},去找最大的满足条件的x。所以,这里可以向上递推的来解决。

  我们还需要维护一个dp[u]表示u这个点向下只走“1”的最远能到达的距离。

10
1 2
1 3
1 4
2 5
2 6
3 7
6 8
8 9
7 10
1 3 3 2 1 1 1 3 1 2
ans:1/1
5
1 2
2 3
3 4
4 5
1 1 2 1 1
ans:2/5
3
1 2
1 3
2 1 1
ans:2/3
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
struct pir
{
    int fir, sec;
    pir(int a=0, int b=0):fir(a), sec(b) {}
    friend bool operator < (pir e1, pir e2) { return e1.fir == e2.fir ? e1.sec > e2.sec : e1.fir > e2.fir; }
    friend bool operator == (pir e1, pir e2) { return e1.fir == e2.fir; }
};
inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
const int maxN = 1e6 + 7;
int N, head[maxN], cnt;
struct Eddge
{
    int nex, to;
    Eddge(int a=-1, int b=0):nex(a), to(b) {}
} edge[maxN << 1];
inline void addEddge(int u, int v)
{
    edge[cnt] = Eddge(head[u], v);
    head[u] = cnt++;
}
inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
int a[maxN], ans[2], deep[maxN], dp[maxN];
pir vt[maxN];
void Min(int *x, int y[2])
{
    if(1LL * x[0] * y[1] > 1LL * x[1] * y[0]) { x[0] = y[0]; x[1] = y[1]; }
}
void dfs(int u, int fa)
{
    int tmp[2];
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa) continue;
        deep[v] = (a[v] == 1 ? deep[u] + 1 : 0);
        dfs(v, u);
        if(a[u] == 1)
        {
            tmp[0] = 1; tmp[1] = dp[u] + 1 + dp[v];
            Min(ans, tmp);
            if(vt[v] < vt[u])
            {
                vt[u] = vt[v];
                if(vt[u].sec + 1 + dp[u] == vt[u].fir)
                {
                    tmp[0] = 2; tmp[1] = 1 + 2 * vt[u].fir;
                    Min(ans, tmp);
                }
            }
            else
            {
                if(vt[u].sec + 1 + dp[v] == vt[u].fir)
                {
                    tmp[0] = 2; tmp[1] = 1 + 2 * vt[u].fir;
                    Min(ans, tmp);
                }
            }
        }
        else if(a[u] == 2)
        {
            tmp[0] = 2; tmp[1] = 2 * min(dp[u], dp[v]) + 1;
            Min(ans, tmp);
        }
        dp[u] = max(dp[u], dp[v]);
    }
    if(a[u] == 1)
    {
        dp[u] ++;
        vt[u].sec ++;
    }
    else if(a[u] == 2)
    {
        if(deep[fa] >= dp[u])
        {
            tmp[0] = 2; tmp[1] = 2 * dp[u] + 1;
            Min(ans, tmp);
        }
        else vt[u] = pir(dp[u], 0);
        dp[u] = 0;
    }
    else { dp[u] = 0; vt[u] = pir(INF, 0); }
    tmp[0] = 1; tmp[1] = dp[u];
    Min(ans, tmp);
}
inline void init()
{
    cnt = 0; ans[0] = INF; ans[1] = 1;
    for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{
    scanf("%d", &N);
    init();
    for(int i=1, u, v; i<N; i++)
    {
        scanf("%d%d", &u, &v);
        _add(u, v);
    }
    for(int i=1; i<=N; i++)
    {
        int tmp[2];
        scanf("%d", &a[i]);
        tmp[0] = a[i]; tmp[1] = 1;
        Min(ans, tmp);
    }
    deep[1] = (a[1] == 1 ? 1 : 0);
    dfs(1, 0);
    int _GCD = gcd(ans[0], ans[1]);
    printf("%d/%d\n", ans[0] / _GCD, ans[1] / _GCD);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值