ecnuoj 5039 摇钱树

5039. 摇钱树#

题目链接:5039. 摇钱树

感觉在赛中的时候,完全没有考虑分数规划这种做法。同时也没有想到怎么拆这两个交和并的式子。有点难受……

当出现分数使其尽量大或者小,并且如果修改其中直接相关的某个值会导致分子分母同时变化的时候,还是要多想想分数规划的做法。

下面引用一下题解

另外这两个交和并的式子,令 a=ST,b=Ta,所以原来的式子变成了

|ST||ST|=ab+|S|

所以,用分数规划的做法,二分一个答案 ans,则有

ab+|S|ansabans|S|ans

接下来用树上 dp 求一个最大的 abans 即可。

fi,j 表示此时 i 号点上选了 j 个子树加和起来最大的 abans 值,xi,j 表示这个式子中的 ayi,j 表示这个式子中的 b

那么接下来就是一个普通的树上背包的转移了,不过区别在于转移完整个 fu 后,需要再用取整个以 u 为根构成的一颗子树去更新一下 fu,1

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef double db;
typedef long double ld;

#define IL inline
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(), (x).end()
#define dbg1(x) cout << #x << " = " << x << ", "
#define dbg2(x) cout << #x << " = " << x << endl

template<typename Tp> IL void read(Tp &x) {
    x=0; int f=1; char ch=getchar();
    while(!isdigit(ch)) {if(ch == '-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    x *= f;
}
int buf[42];
template<typename Tp> IL void write(Tp x) {
    int p = 0;
    if(x < 0) { putchar('-'); x=-x;}
    if(x == 0) { putchar('0'); return;}
    while(x) {
        buf[++p] = x % 10;
        x /= 10;
    }
    for(int i=p;i;i--) putchar('0' + buf[i]);
}

const int N = 100000 + 5;
const int M = 50 + 5;
int n, m;
int a[N], suma[N], sz[N], x[N][M], y[N][M];
db f[N][M];
vector<int> G[N];

struct fs {
    int fz, fm;
    int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b);}
    fs(int fz=0, int fm=1) {
        if(fz == 0) this -> fm = 1;
        else {
            int g = gcd(fz, fm);
            this -> fz = fz / g;
            this -> fm = fm / g;
        }
    }
};

int dfs2(int u, int fa, const db& ef_x) {
    int u_leaf = 0;
    if(u != 1 && G[u].size() == 1) {
        if(a[u] == 1) {
            f[u][1] = 1.0;
            x[u][1] = 1; y[u][1] = 0;
        }
        return ++u_leaf;
    }
    for(int i=0;i<=m;i++) f[u][i] = x[u][i] = y[u][i] = 0;
    for(int v : G[u]) {
        if(v == fa) continue;
        int v_leaf = dfs2(v, u, ef_x);
        for(int i=min(u_leaf,m);i>=0;i--) {
            for(int j=1;j<=v_leaf && i+j <= m; j++) {
                if(f[u][i] + f[v][j] > f[u][i+j]) {
                    f[u][i+j] = f[u][i] + f[v][j];
                    x[u][i+j] = x[u][i] + x[v][j];
                    y[u][i+j] = y[u][i] + y[v][j];
                }
            }
        }
        u_leaf += v_leaf;
    }
    if(suma[u] - (sz[u] - suma[u]) * ef_x >= f[u][1]) {
        f[u][1] = suma[u] - (sz[u] - suma[u]) * ef_x;
        x[u][1] = suma[u];
        y[u][1] = sz[u] - suma[u];
    }
    return u_leaf;
}

array<int, 3> check(const db& x) {
    dfs2(1, 0, x);
    int ansu = 1, ansi = 1;
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) {
        if(f[i][j] > f[ansu][ansi]) {
            ansu = i; ansi = j;
        }
    }
    return {f[ansu][ansi] >= suma[1] * x, ansu, ansi};
}

void dfs1(int u, int fa) {
    suma[u] += a[u];
    sz[u] = 1;
    for(int v : G[u]) {
        if(v == fa) continue;
        dfs1(v, u);
        suma[u] += suma[v];
        sz[u] += sz[v];
    }
}

void solve() {
    read(n); read(m);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<n;i++) {
        int u, v; read(u); read(v);
        G[u].pb(v); G[v].pb(u);
    }
    dfs1(1, 0);
    db L = 0.0, R = 1.0;
    fs ans;
    while(R - L >= 1e-10) {
        db M = (L + R) / 2.0;
        auto arr = check(M);
        if(arr[0]) {L = M; ans = fs(x[arr[1]][arr[2]], y[arr[1]][arr[2]] + suma[1]);}
        else R = M;
    }
    write(ans.fz); putchar(32); write(ans.fm); putchar(10);
}

int main() {
#ifdef LOCAL
    freopen("test.in", "r", stdin);
    // freopen("test.out", "w", stdout);
#endif
    int T = 1;
    // read(T);
    while(T--) solve();
    return 0;
}
posted @   bringlu  阅读(48)  评论(0编辑  收藏  举报
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示
主题色彩