给出两个点数相同的树。若干次询问,每次给出一个点集(\([1,n]\)中的数,一个数表示着两棵树上对应的点),要求得到另一个包含这个点集的点集,满足该点集在两棵树上的点分别联通。
给出权值\(w_i\),求答案点集的\(\sum w_i\)。
\(n\le 10^5\)
一条链+一棵树的部分分做法:类似于之前的连续段问题处理即可(扫右端点,在左端点处维护连通块数,通过点-边得到连通块数,可以线段树上二分找到连续段。将询问丢入以左端点为关键字的大根堆中。对于当前右端点,找到最大左端点使其是连续段,处理左端点包含在其中的询问),时间\(O(n\lg n)\)。
正解:如果集合\(S,T\)合法,则\(S\bigcup T,S\bigcap T\)合法。在包含询问点集的极小连通块中,对于每条边\((u,v)\),找到包含\(u,v\)的极小合法点集,将它们并起来就是答案。
\(T_1\)中的边\((u,v)\)如果被选,需要\(T_2\)中\(u,v\)路径上的所有边被选。反之类似。根据这种选什么就必须选什么这样的关系连有向边(此时原来的边视作一个点),从\((u,v)\)对应点出发,能到达的所有点都要贡献。
用ST表优化连边,跑Tarjan即可。时间\(O(n\lg n)\)。
也可以树剖优化连边,时间\(O(n\lg^2 n)\)但跑得极快。(不过应该也可知用全局平衡二叉树得到\(O(n\lg n)\))
using namespace std;
#include <bits/stdc++.h>
#define N 100005
#define M (N*40)
#define fi first
#define se second
#define mp make_pair
int n,Q;
int w[N];
struct EDGE{
int to;
EDGE *las;
};
template<int V,int E>
struct Graph{
EDGE e[E];
int ne;
EDGE *last[V];
void link(int u,int v){
e[ne]=(EDGE){v,last[u]};
last[u]=e+ne++;
}
};
Graph<M,M*2> F;
int m,c[M];
struct Tree{
Graph<N,N*2> G;
int fa[N][18],dep[N],id[N][18];
void predfs(int x){
for (int i=1;1<<i<=dep[x];++i){
fa[x][i]=fa[fa[x][i-1]][i-1];
c[++m]=max(c[id[x][i-1]],c[id[fa[x][i-1]][i-1]]);
id[x][i]=m;
F.link(id[x][i],id[x][i-1]);
F.link(id[x][i],id[fa[x][i-1]][i-1]);
}
for (EDGE *ei=G.last[x];ei;ei=ei->las)
if (ei->to!=fa[x][0]){
fa[ei->to][0]=x;
c[++m]=max(w[x],w[ei->to]);
id[ei->to][0]=m;
dep[ei->to]=dep[x]+1;
predfs(ei->to);
}
}
void init(){
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
G.link(u,v),G.link(v,u);
}
predfs(1);
}
void lk(int x,int u,int v){
if (dep[u]<dep[v]) swap(u,v);
for (int k=dep[u]-dep[v],i=0;k;k>>=1,++i)
if (k&1){
F.link(x,id[u][i]);
u=fa[u][i];
}
if (u==v) return;
for (int i=17;i>=0;--i)
if (fa[u][i]!=fa[v][i]){
F.link(x,id[u][i]);
F.link(x,id[v][i]);
u=fa[u][i],v=fa[v][i];
}
F.link(x,id[u][0]);
F.link(x,id[v][0]);
}
int qry(int u,int v){
int res=0;
if (dep[u]<dep[v]) swap(u,v);
for (int k=dep[u]-dep[v],i=0;k;k>>=1,++i)
if (k&1){
res=max(res,c[id[u][i]]);
u=fa[u][i];
}
if (u==v) return res;
for (int i=17;i>=0;--i)
if (fa[u][i]!=fa[v][i]){
res=max(res,max(c[id[u][i]],c[id[v][i]]));
u=fa[u][i],v=fa[v][i];
}
res=max(res,max(c[id[u][0]],c[id[v][0]]));
return res;
}
} T[2];
int dfn[M],low[M],nowdfn;
int st[M],tp;
bool ins[M];
void tarjan(int x){
dfn[x]=low[x]=++nowdfn;
st[++tp]=x;
ins[x]=1;
for (EDGE *ei=F.last[x];ei;ei=ei->las){
if (!dfn[ei->to]){
tarjan(ei->to),low[x]=min(low[x],low[ei->to]);
c[x]=max(c[x],c[ei->to]);
}
else if (ins[ei->to])
low[x]=min(low[x],dfn[ei->to]);
else
c[x]=max(c[x],c[ei->to]);
}
if (dfn[x]==low[x]){
int v=c[x];
for (int i=tp;st[i]!=x;--i)
v=max(v,c[st[i]]);
for (;st[tp]!=x;--tp)
ins[st[tp]]=0,c[st[tp]]=v;
ins[x]=0,c[x]=v,--tp;
}
}
int main(){
freopen("orz.in","r",stdin);
freopen("orz.out","w",stdout);
scanf("%d%d",&n,&Q);
for (int i=1;i<=n;++i)
scanf("%d",&w[i]);
T[0].init();
T[1].init();
for (int i=2;i<=n;++i){
T[1].lk(T[0].id[i][0],i,T[0].fa[i][0]);
T[0].lk(T[1].id[i][0],i,T[1].fa[i][0]);
}
for (int i=1;i<=m;++i)
if (!dfn[i])
tarjan(i);
while (Q--){
int k,rt,ans;
scanf("%d%d",&k,&rt);
ans=w[rt];
for (int i=1,x;i<k;++i){
scanf("%d",&x);
ans=max(ans,T[0].qry(rt,x));
}
printf("%d\n",ans);
}
return 0;
}