\(\mathcal{Description}\)

  Link.

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

  \(n\le2\times10^3\),\(m\le10^6\),\(w_u\in[1,m]\),数据组数 \(T\le10\)。

\(\mathcal{Solution}\)

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

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

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

  最终,算法复杂度为 \(\mathcal O(Tn\sqrt m\log n)\)。

\(\mathcal{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;
}

Solution -「HDU 6643」Ridiculous Netizens的更多相关文章

  1. Solution -「HDU 6875」Yajilin

    \(\mathcal{Description}\)   Link.(HDU 裂开了先放个私链 awa.)   在一个 \(n\times n\) 的方格图中,格子 \((i,j)\) 有权值 \(w_ ...

  2. Solution -「HDU 5498」Tree

    \(\mathcal{Description}\)   link.   给定一个 \(n\) 个结点 \(m\) 条边的无向图,\(q\) 次操作每次随机选出一条边.问 \(q\) 条边去重后构成生成 ...

  3. Solution -「HDU 1788」CRT again

    \(\mathcal{Description}\)   Link.   解同余方程组: \[x\equiv m_i-a\pmod{m_i} \]   其中 \(i=1,2,\dots,n\).   \ ...

  4. Solution -「HDU #6566」The Hanged Man

    \(\mathcal{Description}\)   Link.   给定一棵含 \(n\) 个点的树,每个结点有两个权值 \(a\) 和 \(b\).对于 \(k\in[1,m]\),分别求 \[ ...

  5. [HDU多校]Ridiculous Netizens

    [HDU多校]Ridiculous Netizens 点分治 分成两个部分:对某一点P,连通块经过P或不经过P. 经过P采用树形依赖背包 不经过P的部分递归计算 树型依赖背包 v点必须由其父亲u点转移 ...

  6. Solution -「ARC 104E」Random LIS

    \(\mathcal{Description}\)   Link.   给定整数序列 \(\{a_n\}\),对于整数序列 \(\{b_n\}\),\(b_i\) 在 \([1,a_i]\) 中等概率 ...

  7. Solution -「HDU」Professor Ben

    Description 有 \(Q\) 个询问.每次给定一个正整数 \(n\),求它的所有因数的质因数个数的和. Solution 就讲中间的一个 Trick. 我们定义正整数 \(x\) 有 \(f ...

  8. Solution -「CTS 2019」「洛谷 P5404」氪金手游

    \(\mathcal{Description}\)   Link.   有 \(n\) 张卡牌,第 \(i\) 张的权值 \(w_i\in\{1,2,3\}\),且取值为 \(k\) 的概率正比于 \ ...

  9. Solution -「BZOJ 3812」主旋律

    \(\mathcal{Description}\)   Link.   给定含 \(n\) 个点 \(m\) 条边的简单有向图 \(G=(V,E)\),求 \(H=(V,E'\subseteq E)\ ...

随机推荐

  1. android studio 配置 Lombok 插件 -具体步骤

    1.前言 idea 用惯了 Lombok 插件了,好用的很,可是开发安卓 却没有,即便在 android studio 安装了插件,但是仍然无法使用 因为需要配置 2.解决 (1)进入设置,找到插件设 ...

  2. 解决ubuntu18.04重启后蓝牙鼠标需要重新配对的问题

    打开bash,运行bluetoothctl命令 # bluetoothctl 列出可用的蓝牙控制器 [bluetooth]# list 选择使用的蓝牙控制器 [bluetooth]# select 0 ...

  3. 接口神器之 Json Server 详细指南

    简介 json-server 是一款小巧的接口模拟工具,一分钟内就能搭建一套 Restful 风格的 api,尤其适合前端接口测试使用. 只需指定一个 json 文件作为 api 的数据源即可,使用起 ...

  4. kafka学习笔记(二)kafka的基本使用

    概述 第一篇随笔从消息队列的定义和各种应用,以及kafka的分类定义和基本知识,第二篇就写一篇关于kafka的基本实际配置和使用的随笔,包括kafka的集群参数的配置,生产者使用机制,消费者使用机制. ...

  5. Visual Studio 2015 MFC之Button颜色变化-断点调试(Debug)

    软件开发,对自己的程序进行调试很重要,本次文章在上一边随笔的基础上,介绍一下Button控件做显示灯的用法,Button控件的添加和变量设置等可以参考下面的的链接:Visaul Studio 2015 ...

  6. ES6随笔D1

    1.数值解构赋值 ES6 允许按照一定模式,可以从数组中提取值,按照对应位置,对变量赋值,这被称为解构. 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象.由于undefined和n ...

  7. Java集合-ArrayList源码分析

    目录 1.结构特性 2.构造函数 3.成员变量 4.常用的成员方法 5.底层数组扩容原理 6.序列化原理 7.集合元素排序 8.迭代器的实现 9.总结 1.结构特性 Java ArrayList类使用 ...

  8. Prometheus-operator 介绍和配置解析

    随着云原生概念盛行,对于容器.服务.节点以及集群的监控变得越来越重要.Prometheus 作为 Kubernetes 监控的事实标准,有着强大的功能和良好的生态.但是它不支持分布式,不支持数据导入. ...

  9. promise的队列,宏任务,微任务,同步任务

    // promise里面有一个特别的任务,就是微任务 // 同步任务>微任务>宏任务 setTimeout(() => { console.log("setTimeout& ...

  10. Tomcat服务器和Servlet版本的对应关系

    Tomcat服务器和Servlet版本的对应关系 Servlet 程序从2.5版本是现在世面使用最多的版本(xml配置) 到了Servlet3.0后.就是注解版本的Servlet使用