题面

说明/提示

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. Full卷积、Same卷积、Valid卷积、带深度的一维卷积

    转载和参考以下几个链接:https://www.cnblogs.com/itmorn/p/11177439.html; https://blog.csdn.net/jack__linux/articl ...

  2. JavaScript之创建八个对象过520

    马上又到了一年一度的520了,程序猿们赶紧创建对象过520吧!!! JavaScript创建对象的几种方式: 一:字面量方式: var obj = {name: '程序猿'}; 二:通过new操作符: ...

  3. 浅谈倍增法求解LCA

    Luogu P3379 最近公共祖先 原题展现 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入格式 第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数.询问 ...

  4. 你不会用node 操作mysql数据库吗?

    http://static.runoob.com/download/websites.sql这是实例 websites.sql文件1.安装node的mysql服务 npm install mysql ...

  5. 关于我用python表白成功这件事【表白成功】

    520,并非情人所属, 我们可以表白万物, 不管什么时候, 这都是一个特别的日子, 今天,我要表白所有, 心里有我的人! 在这个充满幸福的日子里, 我要把最美好的祝福, 送给心里有我的每一个人: 祝愿 ...

  6. Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现

    前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...

  7. PMP 考试常见工具与技术点总结

    转载请注明出处: 网络图:项目进度活动之间的逻辑关系,用来推算关键路径,最大浮动时间等: 横道图(甘特图):以图示的方式,通过活动列表和时间刻度,来展示项目获得那个顺序和持续时间 责任分配矩阵:每件事 ...

  8. 微信开发在Pc端调用公众号粉丝发送过来的图片素材

    因为项目要在PC端搞一个微信墙功能,就是把粉丝发送过来的上墙内容给展示出来,但因为微信对微信素材进行了防盗链加密处理,所以在非微信页面上直接引用在微信服务器上的图片的链接是无法显示的,只会显示一张微信 ...

  9. linux 运行.sh出现 Permission denied

    执行.sh脚本时提示如下错误: [root@Dolen2021 redis]# ./startRedis.sh -bash: ./startRedis.sh: Permission denied [r ...

  10. APISpace 让你快速获取安徒生童话故事

    <安徒生童话>是丹麦作家安徒生创作的童话集,共由166篇故事组成.该作爱憎分明,热情歌颂劳动人民.赞美他们的善良和纯洁的优秀品德:无情地揭露和批判王公贵族们的愚蠢.无能.贪婪和残暴. 接口 ...