【NOI P模拟赛】最短路(树形DP,树的直径)
题面
给定一棵
n
n
n 个结点的无根树,每条边的边权均为
1
1
1 。
树上标记有
m
m
m 个互不相同的关键点,小
A
\tt A
A 会在这
m
m
m 个点中等概率随机地选择
k
k
k 个不同的点放上小饼干。你想知道,经过有小饼干的
k
k
k 个点的最短路径长度的期望是多少。注意,你可以任意选取起点和终点,路径也可以经过重复的点或重复的边。
2
≤
k
≤
m
≤
n
≤
2000
2\leq k\leq m\leq n\leq2000
2≤k≤m≤n≤2000 。
m
≤
300
m\leq 300
m≤300 ,但是没必要。
题解
我们在脑海中建出这
k
k
k 个点构成的虚树,任意地改变树的形态,会发现,起点和终点一定是某条直径的两端。我们令别的点的
d
f
s
\rm dfs
dfs 序都排在终点前面,然后根据
d
f
s
\rm dfs
dfs 序访问结点。这样,除了直径上的边只经过一次,虚树上别的边都经过了两次。所以“最短路径”等于 边权和×2 减 直径长度。
因此,最短路径长度的期望可以分开来求,先求边权和的期望,再减去直径长度的期望。
边权和的期望很简单,可以树上背包DP,也可以枚举每条边的贡献,前者
O
(
n
2
)
O(n^2)
O(n2) ,后者
O
(
n
)
O(n)
O(n) 。
考虑到总方案数是已知的,我们求所有方案下直径长度的和,可得直径长度的期望。
我们知道,边权为 1 的树的直径有些特点:
- 若直径长度为偶数,则所有直径的中心点一定相同。
- 若直径长度为奇数,则所有直径的中心边一定相同。
因此,我们枚举中心点/边,再枚举直径长度计算,以中心点为例:
令中心点为
x
x
x ,我们从
x
x
x 的每个儿子出发,预处理出每个儿子为根的子树中深度为
i
(
i
≥
1
)
i(i\geq 1)
i(i≥1) 的关键点个数
c
n
t
[
i
]
cnt[i]
cnt[i] ,每个子树分开来。
c
n
t
[
0
]
cnt[0]
cnt[0] 表示
x
x
x 是否为关键点。
若要保证直径长度为
2
l
2l
2l ,则所有
k
k
k 个放上小饼干的点的深度都得小于等于
l
l
l ,同时深度等于
l
l
l 的点至少能选出两个属于不同的子树。我们可以统计所有深度小于
l
l
l 的点数
c
n
t
p
cntp
cntp (包括
c
n
t
[
0
]
cnt[0]
cnt[0]),第
i
i
i 个子树深度等于
l
l
l 的点数为
c
n
t
i
[
l
]
cnt_i[l]
cnti[l] ,用一点小容斥,总数减去
l
l
l 层无点方案再减去
l
l
l 层只有一个子树有点方案。那么直径长度为
2
l
2l
2l 的方案数为
(
c
n
t
p
+
∑
c
n
t
i
[
l
]
k
)
−
(
c
n
t
p
k
)
−
∑
(
(
c
n
t
p
+
c
n
t
i
[
l
]
k
)
−
(
c
n
t
p
k
)
)
{cntp+\sum cnt_i[l]\choose k}-{cntp\choose k}-\sum\left({cntp+cnt_i[l]\choose k}-{cntp\choose k}\right)
(kcntp+∑cnti[l])−(kcntp)−∑((kcntp+cnti[l])−(kcntp))
中心边的算法类似,相当于只有两个子树,还要简单许多。
时间复杂度
O
(
n
2
)
O(n^2)
O(n2) 。
CODE
#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<random>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 2005
#define LL long long
#define ULL unsigned long long
#define DB double
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
#define FI first
#define SE second
LL read() {
LL f=1,x=0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<3) + (x<<1) + (s^48); s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar('0'+(x%10));}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) {putchar('-');x = -x;}
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
const int MOD = 998244353;
int n,m,s,o,k;
int U[MAXN],V[MAXN];
int fac[MAXN],inv[MAXN],invf[MAXN];
int C(int n,int m) {
if(m < 0 || m > n) return 0;
return fac[n] *1ll* invf[n-m] % MOD * invf[m] % MOD;
}
int invC(int n,int m) {
if(m < 0 || m > n) return 1;
return invf[n] *1ll* fac[n-m] % MOD * fac[m] % MOD;
}
int hd[MAXN],v[MAXN<<1],nx[MAXN<<1],cne;
void ins(int x,int y) {
nx[++ cne] = hd[x]; v[cne] = y; hd[x] = cne;
}
int ans = 0;
bool f[MAXN];
int dp[MAXN][MAXN],cc[MAXN][MAXN],sz[MAXN];
void dfs0(int x,int ff) {
cc[x][0] = 1;
sz[x] = f[x];
if(f[x]) cc[x][1] = 1;
int dpp = 0;
for(int i = hd[x];i;i = nx[i]) {
int y = v[i];
if(y != ff) {
dfs0(y,x);
sz[x] += sz[y];
(dpp += MOD-(dp[y][k]+cc[y][k])%MOD) %= MOD;
for(int j = sz[x];j > 0;j --) {
for(int s = 1;s <= sz[y] && s <= j;s ++) {
(dp[x][j] += (dp[x][j-s]*1ll*cc[y][s] + dp[y][s]*1ll*cc[x][j-s] + cc[x][j-s]*1ll*cc[y][s]) % MOD) %= MOD;
(cc[x][j] += cc[x][j-s]*1ll*cc[y][s] % MOD) %= MOD;
}
}
}
}
(dpp += dp[x][k]) %= MOD;
(ans += dpp) %= MOD;
return ;
}
vector<int> bu[MAXN];
int ct[MAXN],mx;
void dfs(int x,int ff,int d) {
if(f[x]) {
mx = max(mx,d);
ct[d] ++;
}
for(int i = hd[x];i;i = nx[i]) {
int y = v[i];
if(y != ff) {
dfs(y,x,d+1);
}
}return ;
}
int main() {
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n = read(); m = read(); k = read();
fac[0]=fac[1]=inv[0]=inv[1]=invf[0]=invf[1]=1;
for(int i = 2;i <= n;i ++) {
fac[i] = fac[i-1] *1ll* i % MOD;
inv[i] = (MOD - inv[MOD%i]) *1ll* (MOD/i) % MOD;
invf[i] = invf[i-1] *1ll* inv[i] % MOD;
}
for(int i = 1;i <= m;i ++) {
f[read()] = 1;
}
for(int i = 1;i < n;i ++) {
s = read();o = read();
U[i] = s; V[i] = o;
ins(s,o); ins(o,s);
}
dfs0(1,0);
ans = ans*2ll % MOD;
for(int i = 1;i < n;i ++) {
s = U[i];o = V[i];
for(int j = 0;j <= n;j ++) bu[j].clear();
dfs(s,o,0);
for(int j = 0;j <= n;j ++) {
bu[j].push_back(ct[j]);
ct[j] = 0;
}
dfs(o,s,0);
for(int j = 0;j <= n;j ++) {
bu[j].push_back(ct[j]);
ct[j] = 0;
}
int cnt = 0;
for(int j = 0;j <= n;j ++) {
int le = (j<<1|1);
int A = bu[j][0],B = bu[j][1];
int nm = (0ll+ C(cnt+A+B,k) +MOD- C(cnt+A,k) +MOD- C(cnt+B,k) + C(cnt,k)) % MOD;
(ans += MOD-nm*1ll*le%MOD) %= MOD;
cnt += A+B;
}
}
for(int i = 1;i <= n;i ++) {
for(int j = 0;j <= n;j ++) bu[j].clear();
for(int y = hd[i];y;y = nx[y]) {
mx = 0;
dfs(v[y],i,1);
for(int j = 1;j <= mx;j ++) {
if(ct[j]) {
bu[j].push_back(ct[j]);
ct[j] = 0;
}
}
}
int cnt = f[i];
for(int j = 1;j <= n;j ++) {
int le = (j<<1),dt = 0;
for(int s = 0;s < (int)bu[j].size();s ++) {
dt += bu[j][s];
}
int O = C(cnt,k);
int nm = (C(cnt+dt,k) +MOD- O) % MOD;
for(int s = 0;s < (int)bu[j].size();s ++) {
(nm += MOD- (C(cnt+bu[j][s],k)+MOD-O)%MOD) %= MOD;
}
(ans += MOD-nm*1ll*le%MOD) %= MOD;
cnt += dt;
}
}
ans = ans *1ll* invC(m,k) % MOD;
AIput(ans,'\n');
return 0;
}
赛时样例太水了,导致我
c
n
t
p
cntp
cntp 未更新的错误没被发现,只有 20 分。

【NOI P模拟赛】最短路(树形DP,树的直径)的更多相关文章
- CSP模拟赛 Repulsed(树形DP)
题面 ⼩ w ⼼⾥的⽕焰就要被熄灭了. 简便起⻅,假设⼩ w 的内⼼是⼀棵 n − 1 条边,n 个节点的树. 现在你要在每个节点⾥放⼀些个灭⽕器,每个节点可以放任意多个. 接下来每个节点都要被分配给 ...
- 4.9 省选模拟赛 圆圈游戏 树形dp set优化建图
由于圆不存在相交的关系 所以包容关系形成了树的形态 其实是一个森林 不过加一个0点 就变成了树. 考虑对于每个圆都求出最近的包容它的点 即他的父亲.然后树形dp即可.暴力建图n^2. const in ...
- computer(树形dp || 树的直径)
Computer Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- POJ 3162.Walking Race 树形dp 树的直径
Walking Race Time Limit: 10000MS Memory Limit: 131072K Total Submissions: 4123 Accepted: 1029 Ca ...
- HDU 2196.Computer 树形dp 树的直径
Computer Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- poj3162 树形dp|树的直径 + 双单调队列|线段树,好题啊
题解链接:https://blog.csdn.net/shiqi_614/article/details/8105149 用树形dp是超时的,, /* 先求出每个点可以跑的最长距离dp[i][0|1] ...
- Computer(HDU2196+树形dp+树的直径)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2196 题目: 题意:有n台电脑,每台电脑连接其他电脑,第i行(包括第一行的n)连接u,长度为w,问你每 ...
- hdu 4607 树形dp 树的直径
题目大意:给你n个点,n-1条边,将图连成一棵生成树,问你从任意点为起点,走k(k<=n)个点,至少需要走多少距离(每条边的距离是1): 思路:树形dp求树的直径r: a:若k<=r+1 ...
- VIJOS1476旅游规划[树形DP 树的直径]
描述 W市的交通规划出现了重大问题,市政府下决心在全市的各大交通路口安排交通疏导员来疏导密集的车流.但由于人员不足,W市市长决定只在最需要安排人员的路口安放人员.具体说来,W市的交通网络十分简单,它包 ...
- Contest Hunter 模拟赛09 C [树形dp+差分]
题面 传送门 思路 又双叒叕是一道差分题我没想出来......记录一下 首先这个"所有祖先都比自己小"等价于"父亲比自己小" 这题的基础dp方程很显然,$dp[ ...
随机推荐
- 全球共有多少MySQL实例在运行?这里有一份数据
摘要 Shadowserver Foundation在5月31日发布了一份全网的MySQL扫描报告,共发现了暴露在公网的360万个MySQL实例.因为这份报告基数够大,而且信息也非常完整,从数据库专业 ...
- Java 基础常见知识点&面试题总结(下),2022 最新版!
你好,我是 Guide.秋招即将到来,我对 JavaGuide 的内容进行了重构完善,同步一下最新更新,希望能够帮助你. 前两篇: Java 基础常见知识点&面试题总结(上),2022 最新版 ...
- 谷歌浏览器控制台 f12怎么设置成中文/英文 切换方法,一定要看到最后!!!
1.打开谷歌浏览器 2.右键选择检查或 f12 打开控制台 3.点击控制台右边的设置 4.中切英 选择偏好设置->语言=>English 5.英切中 6.选择中文 7.重启 8.切换中文成 ...
- bat-winget-win平台的软件包管理器
win10 1709版本以后 引入的包管理器,如果不可用 需要 更新一下 应用安装程序. winget命令的功能 常用的就 安装 卸载 更新 . 卸载 使用中如果提示 策略 不允许,可执行下面命 ...
- -bash: /usr/local/maven/apache-maven-3.8.1/bin/mvn: 权限不够
chmod a+x /usr/local/maven/apache-maven-3.8.1/bin/mvn
- ASP.NET Core 根据环境变量支持多个 appsettings.json配置文件 (开发和生产)
新建一个项目,web根目录会出现一个 appsettings.json 配置文件, 此时添加--新建项,输入 appsettings.Development.json 再新增一个,appsetti ...
- 步态识别《GaitSet: Regarding Gait as a Set for Cross-View Gait Recognition》2018 CVPR
Motivation: 步态可被当作一种可用于识别的生物特征在刑侦或者安全场景发挥重要作用.但是现有的方法要么是使用步态模板(能量图与能量熵图等)导致时序信息丢失,要么是要求步态序列连续,导致灵活性差 ...
- Map接口中的常用方法和Map集合遍历键找值方式
Map接口中定义了很多方法,常用的如下: public V put(K key,V value) 将指定的值与此映射中的指定键相关联(可选操作) V remove(Object key); 如果此映射 ...
- Linux环境下ProxyChains应用网络代理
1.下载源码 git clone https://github.com.cnpmjs.org/rofl0r/proxychains-ng.git 或者 https://hub.fastgit.org/ ...
- Chrome安装Vue.js devtool F12无效
要安装 vue-devtools-4.1.4_0 链接: https://pan.baidu.com/s/1aeUxKJEUDW0U_i6uuAZFvQ 提取码: 4btc vue-devtools- ...