题面

说明/提示

N<=300000, q<=300000,m[1]+m[2]+...+m[q]<=300000

题解

这道题一看 “m[1]+m[2]+...+m[q]<=300000” 就知道可以用虚树做,利用每个关键点和其lca和树根建一棵点数为2 * m[i] - 1的虚树,非关键部分深度为logm,所以我们把关键点们按深度从小到大排序,

我们用dp[x]记录以x为根的子树中深度最小的点,

然后我们发现,每一个点可以一路高攀,访问完没有被访问过的点,然后根据一路分支上的其它子树的dp值,来一路判定势力范围,然后更新答案,每次的复杂度是O(mlogm)。

CODE

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#define LL long long
#define MAXN 300005
using namespace std;
inline int read() {
int f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();}
while(s >= '0' && s <= '9') {x = x * 10 + s - '0';s = getchar();}
return x * f;
}
struct ed{
int v,w;
ed(){v = w = 0;}
ed(int V,int W){v = V;w = W;}
};
vector<ed> g[MAXN];
vector<ed> g2[MAXN];
int fa[MAXN][22];
int fu[MAXN];
int pup[MAXN][22];
int d[MAXN],dfn[MAXN],sons[MAXN];
int n,m,i,j,s,o,k,cnt;
LL dp[MAXN][10];
int b[MAXN];
int b2[MAXN];
int b3[MAXN];
bool cmp(int a,int b) {
return dfn[a] < dfn[b];
}
bool cmp2(int x,int y) {
if(d[b[x]] != d[b[y]])return d[b[x]] < d[b[y]];
return b[x] < b[y];
}
void dfs(int x,int fat,int edge) {
dfn[x] = ++cnt;
fa[x][0] = fat;
d[x] = d[fat] + 1;
pup[x][0] = 1;
sons[x] = 1;
for(int i = 1;i <= 19;i ++) {
fa[x][i] = fa[fa[x][i - 1]][i - 1];
pup[x][i] = 1;
}
for(int i = 0;i < g[x].size();i ++) {
if(g[x][i].v != fat) {
dfs(g[x][i].v,x,g[x][i].w);
int y = g[x][i].v;
sons[x] += sons[y];
}
}
}
void dfsp(int x,int fat) {
pup[x][0] = sons[fat] - sons[x];
for(int i = 1;i <= 19;i ++) {
pup[x][i] = pup[x][i - 1] + pup[fa[x][i - 1]][i - 1];
}
for(int i = 0;i < g[x].size();i ++) {
if(g[x][i].v != fat) {
dfsp(g[x][i].v,x);
}
}
return ;
}
int lca(int a,int b) {
if(d[a] > d[b]) {
for(int i = 19;i >= 0;i --) {
if(d[fa[a][i]] >= d[b]) a = fa[a][i];
}
}
if(d[a] < d[b]) {
for(int i = 19;i >= 0;i --) {
if(d[fa[b][i]] >= d[a]) b = fa[b][i];
}
}
if(a == b) return a;
for(int i = 19;i >= 0;i --) {
if(fa[a][i] != fa[b][i]) {
a = fa[a][i];
b = fa[b][i];
}
}
return fa[a][0];
}
stack<int> st;
int v[MAXN],f[MAXN];
int as[MAXN],asf[MAXN];
void dfs2(int x) {
dp[x][0] = 0;
f[x] = 0;asf[x] = 0;as[x] = 0;
if(v[x]) dp[x][0] = v[x];
for(int i = 0;i < g2[x].size();i ++) {
ed y = g2[x][i];
// printf("%d %d(%lld)\n",x,y.v,y.w);
dfs2(y.v);
fu[y.v] = x;
if(d[dp[y.v][0]] < d[dp[x][0]] || (d[dp[y.v][0]] == d[dp[x][0]] && dp[y.v][0] < dp[x][0])) {
dp[x][0] = dp[y.v][0];
}
}
// printf("%d (%lld %lld)\n",x,dp[x][0],dp[x][1]);
return ;
}
int getsum(int a,int de) {
int ans = 0;
for(int i = 19;i >= 0;i --) {
if(d[fa[a][i]] >= de) ans += pup[a][i],a = fa[a][i];
}
return ans;
}
int getfa(int a,int de) {
for(int i = 19;i >= 0;i --) {
if(d[fa[a][i]] >= de) a = fa[a][i];
}
return a;
}
int dfs3(int x,int fat,int de,int pp) {
f[x] = 1;
// printf("(-%d-)",x);
int ans = sons[x];
for(int i = 0;i < g2[x].size();i ++) {
ed y = g2[x][i];
ans -= sons[y.v] + getsum(y.v,d[x] + 1);
// printf("( (in)ans:%d )",ans);
if(y.v != fat) {
int z = dp[y.v][0];
int nm = de + d[z] - d[x];
int nm2;
if(nm%2) {
nm2 = (nm / 2) - de;
}
else {
nm2 = (nm / 2) - de;
if(pp > z) nm2 --;
}
// printf("( (in)ans:%d nm2:%d nm:%d z:%d )",ans,nm2,nm,z);
if(nm2 < y.w) {
ans += getsum(getfa(y.v,d[x] + nm2 + 1),d[x] + 1);
asf[y.v] = getsum(y.v,d[x] + nm2 + 1);
}
else ans += getsum(y.v,d[x] + 1) + dfs3(y.v,x,de + y.w,pp);
}
}
return ans;
}
void qkdfs(int x) {
for(int i = 0;i < g2[x].size();i ++) qkdfs(g2[x][i].v);
g2[x].clear();return ;
}
int main() {
n = read();
for(int i = 1;i < n;i ++) {
s = read();o = read();
g[s].push_back(ed(o,1));
g[o].push_back(ed(s,1));
}
dfs(1,1,0x7f7f7f7f);
dfsp(1,1);
m = read();
while(m --) {
cnt = read();
for(int i = 1;i <= cnt;i ++) {
b3[i] = b[i] = read();
}sort(b + 1,b + 1 + cnt,cmp);
st.push(1);
for(int i = 1;i <= cnt;i ++) {
v[b[i]] = b[i];
int y1 = st.top();
int lc = lca(y1,b[i]);
int pp = 0,p = 0;
while(!st.empty() && d[st.top()] >= d[lc]) {
pp = p;p = st.top();
st.pop();
if(pp) {
if(pp != p)g2[p].push_back(ed(pp,d[pp] - d[p]));
}
}
if(p && p != lc) {
g2[lc].push_back(ed(p,d[p] - d[lc]));
}
st.push(lc);
st.push(b[i]);
}
int p = 0;
while(!st.empty()) {
int y1 = st.top();
if(p) {
if(p != y1)g2[y1].push_back(ed(p,d[p] - d[y1]));
}
p = y1;
st.pop();
}
d[0] = 0x7f7f7f7f;
dfs2(1);
fu[1] = 0;f[0] = 1;
for(int i = 1;i <= cnt;i ++) b2[i] = i;
sort(b2 + 1,b2 + 1 + cnt,cmp2);
for(int ii = 1;ii <= cnt;ii ++) {
int i = b2[ii];
int p = b[i];
// printf("%d: ",p);
int pp = 0;
while(p) {
as[b[i]] += dfs3(p,pp,d[b[i]] - d[p],b[i]);
// printf("(%d)",as[b[i]]);
if(!f[fu[p]]) {
as[b[i]] += getsum(p,d[fu[p]] + 1);
// printf("([1]%d)",as[b[i]]);
}
else {
as[b[i]] += asf[p];
// printf("([2]%d)",as[b[i]]);
break;
}
pp = p;
p = fu[p];
}
// putchar('\n');
}
for(int i = 1;i <= cnt;i ++) {
printf("%d ",as[b3[i]]);
}putchar('\n');
for(int i = 1;i <= cnt;i ++) v[b[i]] = 0,as[b[i]] = 0;
qkdfs(1);
}
return 0;
}

Hnoi2014世界树的更多相关文章

  1. <虚树+树型DP> HNOI2014世界树

    <虚树+树型DP> HNOI2014世界树 #include <iostream> #include <cstdio> #include <cstring&g ...

  2. bzoj 3572 [Hnoi2014]世界树(虚树+DP)

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 645  Solved: 362[Submit][Status] ...

  3. bzoj3572[Hnoi2014] 世界树 虚树+dp+倍增

    [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1921  Solved: 1019[Submit][Status][Dis ...

  4. bzoj 3572: [Hnoi2014]世界树 虚树 && AC500

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 520  Solved: 300[Submit][Status] ...

  5. 【BZOJ3572】[Hnoi2014]世界树 虚树

    [BZOJ3572][Hnoi2014]世界树 Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森 ...

  6. BZOJ 2286 消耗战 - 虚树 + 树型dp

    传送门 题目大意: 每次给出k个特殊点,回答将这些特殊点与根节点断开至少需要多少代价. 题目分析: 虚树入门 + 树型dp: 刚刚学习完虚树(好文),就来这道入门题签个到. 虚树就是将树中的一些关键点 ...

  7. <虚树+树型DP> SDOI2011消耗战

    <虚树+树型DP> SDOI2011消耗战 #include <iostream> #include <cstdio> #include <cstring&g ...

  8. POJ3659 Cell Phone Network(树上最小支配集:树型DP)

    题目求一棵树的最小支配数. 支配集,即把图的点分成两个集合,所有非支配集内的点都和支配集内的某一点相邻. 听说即使是二分图,最小支配集的求解也是还没多项式算法的.而树上求最小支配集树型DP就OK了. ...

  9. POJ 3342 - Party at Hali-Bula 树型DP+最优解唯一性判断

    好久没写树型dp了...以前都是先找到叶子节点.用队列维护来做的...这次学着vector动态数组+DFS回朔的方法..感觉思路更加的清晰... 关于题目的第一问...能邀请到的最多人数..so ea ...

  10. bzoj 2286: [Sdoi2011]消耗战 虚树+树dp

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MB[Submit][Status][Discuss] Description 在一 ...

随机推荐

  1. [react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?

    壹 ❀ 引 虚拟DOM(Virtual DOM)在前端领域也算是老生常谈的话题了,若你了解过vue或者react一定避不开这个话题,因此虚拟DOM也算是面试中常问的一个点,那么通过本文,你将了解到如下 ...

  2. 【Github】 Github访问不是私密连接问题

    前言 GitHub是一个软件项目的托管平台,是我们经常需要访问的,我原本在学校时候虽然网速比较慢,但是还以能够满足一些代码下载和上传的,在暑假回到家,再去访问的时候就出现了不能访问的问题. 问题描述 ...

  3. 2 万字 + 20张图| 细说 Redis 九种数据类型和应用场景

    作者:小林coding 计算机八股文网(操作系统.计算机网络.计算机组成.MySQL.Redis):https://xiaolincoding.com 大家好,我是小林. 我们都知道 Redis 提供 ...

  4. 一张图进阶 RocketMQ - 整体架构

    前 言 三此君看了好几本书,看了很多遍源码整理的 一张图进阶 RocketMQ 图片链接,关于 RocketMQ 你只需要记住这张图!如果你第一次看到这个系列,墙裂建议你打开链接.觉得不错的话,记得点 ...

  5. 腾讯视频的qlv格式转换为mp4格式

    1.点击设置->下载设置->缓存管理 下的文件目录复制; 2复制在 我的电脑路径栏目中 找到缓存目录 文件夹vodcache; 3.打开视频对应文件; 4.打开cmd命令窗口 5.跳转 到 ...

  6. UiPath鼠标操作元素的介绍和使用

    一.鼠标(mouse)操作的介绍 模拟用户使用鼠标操作的一种行为,例如单击,双击,悬浮.根据作用对象的不同我们可以分为对元素的操作.对文本的操作和对图像的操作 二.鼠标对元素的操作在UiPath中的使 ...

  7. 你真的会python中的for循环吗

    for 循环是 Python 中的通用序列迭代器:它可以单步遍历任何有序序列中的元素.for 语句适用于字符串.列表.元组.其他内置可迭代对象和类创建的新对象. for 通常比 while 循环更容易 ...

  8. Google Colab初次使用

    网页无法加载,出现HTTP ERROR 407 开启chrome时不要在最下面的固定栏打开,否则会出错.

  9. 函数式(Functional)接口

    public class LambdaTest2 { @Test public void test1(){ happyTime(500, new Consumer<Double>() { ...

  10. 一切皆为字节和字节输出流_OutputStream类&FileOutputStream类介绍

    一切皆为字节 一切文件数据(文本.图片.视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此.所以,字节流可以传输任意文件数据.在操作流的时候,我们要时刻明确,无论使用什 ...