Live2D

Solution -「HDU 6643」Ridiculous Netizens

Description

  Link.

  给定一棵含有 n 个结点的树,点 u 有点权 wu,求树上非空连通块的数量,使得连通块内点权积 m

  n2×103m106wu[1,m],数据组数 T10

Solution

  很明显是点分,每次考虑跨当前分治重心 r 的所有连通块对答案的贡献。问题变为:求树上以 r 为根的满足条件的连通块数量。

  一个简单的想法是以子树为子问题树上 DP,但是点权积的状态空间与子树大小完全无关,子树与子树的合并反而更加浪费时间,这提示我们,应该设计一种仅有单点更新的 DP 状态——以 DFN 为子问题 DP。

  另一方面,由于运算全部是乘法,可以考虑整除分块的储存方式压缩状态树。令 f(u,i) 表示当 DFS 进行到某一时刻时,以 u 子树内已经被搜过的点为最大 DFN 点的连通块中,点权积在整除分块后被映射到 i 的方案数。进入 u 子树时用 u 的父亲更新 f(u),退出 u 子树时将 f(u) 上传给 u 的父亲。设树的大小为 s,DP 的复杂度为 O(sm)

  最终,算法复杂度为 O(Tnmlogn)

Code

/*+Rainybunny+*/

#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)

typedef long long LL;

const int MAXN = 2e3, MOD = 1e9 + 7, THRES = 1e3;
int n, m, thres, ecnt, val[MAXN + 5], head[MAXN + 5];
int siz[MAXN + 5], wgt[MAXN + 5], ans;
int f[MAXN + 5][THRES * 2 + 5], g[MAXN + 5][THRES * 2 + 5];
struct Edge { int to, nxt; } graph[MAXN * 2 + 5];
bool vis[MAXN + 5];

inline void chkmax(int& u, const int v) { u < v && (u = v); }
inline int imin(const int u, const int v) { return u < v ? u : v; }
inline void addeq(int& u, const int v) { (u += v) >= MOD && (u -= MOD); }

inline void link(const int u, const int v) {
    graph[++ecnt] = { v, head[u] }, head[u] = ecnt;
    graph[++ecnt] = { u, head[v] }, head[v] = ecnt;
}

inline void findG(const int u, const int fa, const int all, int& rt) {
    siz[u] = 1, wgt[u] = 0;
    for (int i = head[u], v; i; i = graph[i].nxt) {
        if (!vis[v = graph[i].to] && v != fa) {
            findG(v, u, all, rt), siz[u] += siz[v];
            chkmax(wgt[u], siz[v]);
        }
    }
    chkmax(wgt[u], all - siz[u]);
    if (!rt || wgt[rt] > wgt[u]) rt = u;
}

inline void getDP(const int u, const int fa) {
    int *fcur = f[u], *ffa = f[fa];
    rep (i, 0, thres << 1) fcur[i] = 0;
    if (!fa) fcur[val[u] <= thres ? val[u] : thres + m / val[u]] = 1;
    else {
        rep (i, 0, imin(thres, m / val[u])) {
            int t = i * val[u];
            addeq(fcur[t <= thres ? t : thres + m / t], ffa[i]);
        }
        rep (i, val[u], thres) {
            addeq(fcur[thres + i / val[u]], ffa[thres + i]);
        }
    }
    for (int i = head[u], v; i; i = graph[i].nxt) {
        if (!vis[v = graph[i].to] && v != fa) {
            getDP(v, u);
        }
    }
    if (fa) rep (i, 0, thres << 1) addeq(ffa[i], fcur[i]);
}

inline void solve(const int u) {
    // printf("!%d\n", u);
    vis[u] = true, getDP(u, 0);
    rep (i, 0, thres << 1) addeq(ans, f[u][i]);
    for (int i = head[u], v, rt; i; i = graph[i].nxt) {
        if (!vis[v = graph[i].to]) {
            findG(v, 0, siz[v], rt = 0), solve(rt);
        }
    }
}

inline void allClear() {
    ans = ecnt = 0;
    rep (i, 1, n) head[i] = vis[i] = 0;
}

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &n, &m), thres = int(sqrt(1. * m));
        allClear();
        rep (i, 1, n) scanf("%d", &val[i]);
        rep (i, 2, n) { int u, v; scanf("%d %d", &u, &v), link(u, v); }
        int rt = 0; findG(1, 0, n, rt);
        solve(rt), printf("%d\n", ans);
    }
    return 0;
}
posted @   Rainybunny  阅读(91)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示