题面

说明/提示

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. RMQ——ST表

    ST表 ST表是一种解决RMQ问题的强有力工具, 可以做到O(nlogn)预处理,O(1)查询. st[i][j] 表示区间 [i, i + 2 ^ j - 1] 的最大值. 初值 st[i][0] ...

  2. HashMap高阶用法,十倍提升开发效率

    HashMap在工作中使用非常频繁,其实在JDK1.8的时候新增一些更高阶的用法,熟练使用这些方法可以大大提升开发效率,写出更简洁优美的代码. 1. get方法指定返回默认值(getOrDefault ...

  3. Java集合框架(四)-HashMap

    1.HashMap特点 存放的元素都是键值对(key-value),key是唯一的,value是可以重复的 存放的元素也不保证添加的顺序,即是无序的 存放的元素的键可以为null,但是只能有一个key ...

  4. RPA应用场景-海关报关

    场景概述海关报关 所涉系统名称海关页面,业务核心系统 人工操作(时间/次) 10 分钟 所涉人工数量 3 操作频率实时 场景流程 1.每日接收报关申请邮件: 2.根据邮件信息进入业务核心系统查询相关数 ...

  5. MySql字段增删改语句

    新增表字段:alter table 表名 需要添加的字段信息; ALTER TABLE nation add seq VARCHAR(20) COMMENT '顺序' 字段名的修改:alter tab ...

  6. 使用Navicat创建存储过程(顺带插入百万级数据量)

    一.建表 DROP TABLE IF EXISTS `test_user`; CREATE TABLE `test_user` ( `id` bigint(20) PRIMARY key not nu ...

  7. [.NET6]使用ML.NET+ONNX预训练模型整活B站经典《华强买瓜》

    最近在看微软开源的机器学习框架ML.NET使用别人的预训练模型(开放神经网络交换格式.onnx)来识别图像,然后逛github发现一个好玩的repo.决定整活一期博客. 首先还是稍微科普一下机器学习相 ...

  8. String类型函数传递问题

    String类型函数传递问题 问题 以前没有注意过的一个问题, 最近在使用String类型作为函数入参的时候, 发现函数内对于String类型的改变并不会影响到外层调用对象本身; 结论 (先说结论) ...

  9. 05 MySQL_主键约束

    主键约束 主键: 用于表示数据唯一性的字段称为主键: 约束:就是对表字段添加限制条件 主键约束:保证主键字段的值唯一且非空: - 格式 : create table t1(id int primary ...

  10. 【中国象棋人机对战】引入了AI算法,学习低代码和高代码如何混编并互相调用

    以低代码和高代码(原生JS代码)混编的方式引入了AI算法,学习如何使用表达式调用原生代码的.整个过程在众触低代码应用平台进行,适合高阶学员. AI智能级别演示 AI算法分三个等级,体现出来的智能水平不 ...