题面

说明/提示

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. 用Typescript 的方式封装Vue3的表单绑定,支持防抖等功能。

    Vue3 的父子组件传值.绑定表单数据.UI库的二次封装.防抖等,想来大家都很熟悉了,本篇介绍一种使用 Typescript 的方式进行统一的封装的方法. 基础使用方法 Vue3对于表单的绑定提供了一 ...

  2. centos通过日志查入侵

    1. Linux查看/var/log/wtmp文件查看可疑IP登陆 last -f /var/log/wtmp 该日志文件永久记录每个用户登录.注销及系统的启动.停机的事件.因此随着系统正常运行时间的 ...

  3. Java已知图片路径下载图片到本地

    public static void main(String[] args) { FileOutputStream fos = null; BufferedInputStream bis = null ...

  4. NC16462 [NOIP2015]跳石头

    NC16462 [NOIP2015]跳石头 题目 题目描述 一年一度的"跳石头"比赛又要开始了! 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块 ...

  5. Cayley 定理与扩展 Cayley 定理

    Cayley 定理 节点个数为 \(n\) 的无根标号树的个数为 \(n^{n−2}\) . 这个结论在很多计数类题目中出现,要证明它首先需要了解 \(\text{Prufer}\) 序列的相关内容. ...

  6. 静态代码块和数组工具类Arrays

    静态代码块 静态代码块:定义在成员位置,使用static修饰的代码块{ }. ~位置:类中方法外. ~执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行 格式: public cl ...

  7. Java开发学习(十一)----基于注解开发bean作用范围与生命周期管理

    一.注解开发bean作用范围与生命周期管理 前面使用注解已经完成了bean的管理,接下来将通过配置实现的内容都换成对应的注解实现,包含两部分内容:bean作用范围和bean生命周期. 1.1 环境准备 ...

  8. NOI / 2.1基本算法之枚举-8759:火车上的人数

    8759:火车上的人数​​​​​​ 总时间限制: 1000ms 内存限制: 65536kB 描述 火车从始发站(称为第1站)开出,在始发站上车的人数为a,然后到达第2站,在第2站有人上.下车,但上.下 ...

  9. SpringCloud微服务实战——搭建企业级开发框架(四十五):【微服务监控告警实现方式二】使用Actuator(Micrometer)+Prometheus+Grafana实现完整的微服务监控

      无论是使用SpringBootAdmin还是使用Prometheus+Grafana都离不开SpringBoot提供的核心组件Actuator.提到Actuator,又不得不提Micrometer ...

  10. 2535-springsecurity系列--关于授权角色“ROLE”前缀的问题

    版本信息 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring ...